Merge branch 'dev' of github.com:molanp/zhenxun_bot into dev

This commit is contained in:
molanp 2024-10-05 13:56:02 +08:00
commit 513c5eb0fe
17 changed files with 404 additions and 80 deletions

1
__version__ Normal file
View File

@ -0,0 +1 @@
__version__: v0.2.3-ea00860

View File

@ -1,16 +1,21 @@
import uuid
from datetime import datetime
import nonebot
import ujson as json
from nonebot import require
from nonebot.drivers import Driver
from tortoise import Tortoise
from nonebot.adapters import Bot
from nonebot.drivers import Driver
from tortoise.exceptions import OperationalError
from zhenxun.models.goods_info import GoodsInfo
from zhenxun.models.group_member_info import GroupInfoUser
from zhenxun.models.sign_user import SignUser
from zhenxun.models.user_console import UserConsole
from zhenxun.services.log import logger
from zhenxun.models.sign_user import SignUser
from zhenxun.models.goods_info import GoodsInfo
from zhenxun.models.user_console import UserConsole
from zhenxun.utils.decorator.shop import shop_register
from zhenxun.models.bot_connect_log import BotConnectLog
from zhenxun.models.group_member_info import GroupInfoUser
require("nonebot_plugin_apscheduler")
require("nonebot_plugin_alconna")
@ -19,14 +24,28 @@ require("nonebot_plugin_userinfo")
require("nonebot_plugin_htmlrender")
import nonebot
import ujson as json
driver: Driver = nonebot.get_driver()
@driver.on_bot_connect
async def _(bot: Bot):
logger.debug(f"Bot: {bot.self_id} 建立连接...")
await BotConnectLog.create(
bot_id=bot.self_id, platform=bot.adapter, connect_time=datetime.now(), type=1
)
@driver.on_bot_disconnect
async def _(bot: Bot):
logger.debug(f"Bot: {bot.self_id} 断开连接...")
await BotConnectLog.create(
bot_id=bot.self_id, platform=bot.adapter, connect_time=datetime.now(), type=0
)
SIGN_SQL = """
select distinct on("user_id") t1.user_id, t1.checkin_count, t1.add_probability, t1.specify_probability, t1.impression
select distinct on("user_id") t1.user_id, t1.checkin_count, t1.add_probability,
t1.specify_probability, t1.impression
from public.sign_group_users t1
join (
select user_id, max(t2.impression) as max_impression
@ -74,15 +93,12 @@ async def _():
}
create_list = []
sign_id_list = []
max_uid = 0
if user2uid:
max_uid = max(user2uid.values()) + 1
max_uid = max(user2uid.values()) + 1 if user2uid else 0
for old_sign in old_sign_list:
sign_id_list.append(old_sign["user_id"])
old_bag = [
if old_bag := [
b for b in old_bag_list if b["user_id"] == old_sign["user_id"]
]
if old_bag:
]:
old_bag = old_bag[0]
property = json.loads(old_bag["property"])
props = {}
@ -115,9 +131,9 @@ async def _():
create_list.clear()
uc_dict = {u.user_id: u for u in await UserConsole.all()}
for old_sign in old_sign_list:
user_console = uc_dict.get(old_sign["user_id"])
if not user_console:
user_console = await UserConsole.get_user(old_sign["user_id"], "qq")
user_console = uc_dict.get(
old_sign["user_id"]
) or await UserConsole.get_user(old_sign["user_id"], "qq")
create_list.append(
SignUser(
user_id=old_sign["user_id"],

View File

@ -1,16 +1,18 @@
from datetime import datetime
from nonebot.adapters import Bot, Event
from nonebot.adapters.onebot.v11 import PokeNotifyEvent
from nonebot.matcher import Matcher
from nonebot.message import run_postprocessor
from nonebot.adapters import Bot, Event
from nonebot.plugin import PluginMetadata
from nonebot.message import run_postprocessor
from nonebot_plugin_session import EventSession
from nonebot_plugin_apscheduler import scheduler
from nonebot.adapters.onebot.v11 import PokeNotifyEvent
from zhenxun.services.log import logger
from zhenxun.utils.enum import PluginType
from zhenxun.models.statistics import Statistics
from zhenxun.configs.utils import PluginExtraData
from zhenxun.models.plugin_info import PluginInfo
from zhenxun.models.statistics import Statistics
from zhenxun.utils.enum import PluginType
__plugin_meta__ = PluginMetadata(
name="功能调用统计",
@ -21,6 +23,8 @@ __plugin_meta__ = PluginMetadata(
).dict(),
)
TEMP_LIST = []
@run_postprocessor
async def _(
@ -33,16 +37,32 @@ async def _(
if matcher.type == "notice" and not isinstance(event, PokeNotifyEvent):
"""过滤除poke外的notice"""
return
if session.id1:
plugin = await PluginInfo.get_or_none(module=matcher.module_name)
if session.id1 and matcher.plugin:
plugin = await PluginInfo.get_plugin(module_path=matcher.plugin.module_name)
plugin_type = plugin.plugin_type if plugin else None
if plugin_type == PluginType.NORMAL and matcher.plugin_name not in [
"update_info",
"statistics_handle",
]:
await Statistics.create(
if plugin_type == PluginType.NORMAL:
logger.debug(f"提交调用记录: {matcher.plugin_name}...", session=session)
TEMP_LIST.append(
Statistics(
user_id=session.id1,
group_id=session.id3 or session.id2,
plugin_name=matcher.plugin_name,
create_time=datetime.now(),
bot_id=bot.self_id,
)
)
@scheduler.scheduled_job(
"interval",
minutes=1,
)
async def _():
try:
call_list = TEMP_LIST.copy()
TEMP_LIST.clear()
if call_list:
await Statistics.bulk_create(call_list)
logger.debug(f"批量添加调用记录 {len(call_list)}", "定时任务")
except Exception as e:
logger.error("定时批量添加调用记录", "定时任务", e=e)

View File

@ -1,9 +1,18 @@
from datetime import datetime, timedelta
from nonebot import require
from fastapi import APIRouter
from tortoise.functions import Count
from tortoise.expressions import RawSQL
from zhenxun.models.statistics import Statistics
from zhenxun.models.chat_history import ChatHistory
from zhenxun.models.bot_connect_log import BotConnectLog
from ....base_model import Result
from .data_source import BotManage
from ....utils import authentication
from ....base_model import Result, QueryModel, BaseResultModel
from .model import ChatCallMonthCount, QueryChatCallCount, AllChatAndCallCount
require("plugin_store")
@ -20,3 +29,142 @@ async def _() -> Result:
return Result.ok(await BotManage.get_bot_list(), "拿到信息啦!")
except Exception as e:
return Result.fail(f"发生了一点错误捏 {type(e)}: {e}")
@router.get(
"/get_chat_and_call_count",
dependencies=[authentication()],
description="获取聊天/调用记录的全部和今日数量",
)
async def _(bot_id: str | None = None) -> Result:
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,
)
)
@router.get(
"/get_all_chat_and_call_count",
dependencies=[authentication()],
description="获取聊天/调用记录的全部数据次数",
)
async def _(bot_id: str | None = None) -> Result:
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,
)
)
@router.get(
"/get_chat_and_call_month",
dependencies=[authentication()],
deprecated="获取聊天/调用记录的一个月数量", # type: ignore
)
async def _() -> Result:
now = datetime.now()
filter_date = now - timedelta(days=30, hours=now.hour, minutes=now.minute)
chat_date_list = (
await ChatHistory.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 Statistics.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)
)
@router.post(
"/get_connect_log",
dependencies=[authentication()],
deprecated="获取Bot连接记录", # type: ignore
)
async def _(query: QueryModel) -> Result:
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))

View File

@ -62,10 +62,7 @@ class BotManage:
bot_info.connect_time = bot_live.get(bot.self_id) or 0
if bot_info.connect_time:
connect_date = datetime.fromtimestamp(CONNECT_TIME)
connect_date_str = connect_date.strftime("%Y-%m-%d %H:%M:%S")
bot_info.connect_date = datetime.strptime(
connect_date_str, "%Y-%m-%d %H:%M:%S"
)
bot_info.connect_date = connect_date.strftime("%Y-%m-%d %H:%M:%S")
return bot_info
@classmethod

View File

@ -1,5 +1,3 @@
from datetime import datetime
from pydantic import BaseModel
@ -22,5 +20,52 @@ class BotInfo(BaseModel):
"""今日调用插件次数"""
connect_time: int = 0
"""连接时间"""
connect_date: datetime | None = None
connect_date: str | None = None
"""连接日期"""
class QueryChatCallCount(BaseModel):
"""
查询聊天/调用记录次数
"""
chat_num: int
"""聊天记录总数"""
chat_day: int
"""今日消息"""
call_num: int
"""调用记录总数"""
call_day: int
"""今日调用"""
class ChatCallMonthCount(BaseModel):
"""
查询聊天/调用一个月记录次数
"""
chat: list[int]
"""一个月内聊天总数"""
call: list[int]
"""一个月内调用数据"""
date: list[str]
"""日期"""
class AllChatAndCallCount(BaseModel):
"""
查询聊天/调用记录次数
"""
chat_week: int
"""一周内聊天次数"""
chat_month: int
"""一月内聊天次数"""
chat_year: int
"""一年内聊天次数"""
call_week: int
"""一周内调用次数"""
call_month: int
"""一月内调用次数"""
call_year: int
"""一年内调用次数"""

View File

@ -21,8 +21,9 @@ from ....base_model import Result
from .data_source import bot_live
from ....utils import authentication, get_system_status
from ....config import AVA_URL, GROUP_AVA_URL, QueryDateType
from .model import BaseInfo, HotPlugin, ActiveGroup, ChatHistoryCount
from .model import BaseInfo, HotPlugin, QueryCount, ActiveGroup, NonebotData
driver = nonebot.get_driver()
run_time = time.time()
ws_router = APIRouter()
@ -39,6 +40,7 @@ async def _(bot_id: str | None = None) -> Result:
返回:
Result: 获取指定bot信息与bot列表
"""
global run_time
bot_list: list[BaseInfo] = []
if bots := nonebot.get_bots():
select_bot: BaseInfo
@ -82,10 +84,7 @@ async def _(bot_id: str | None = 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)
connect_date_str = connect_date.strftime("%Y-%m-%d %H:%M:%S")
select_bot.connect_date = datetime.strptime(
connect_date_str, "%Y-%m-%d %H:%M:%S"
)
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():
@ -102,23 +101,58 @@ async def _(bot_id: str | None = None) -> Result:
@router.get(
"/get_all_ch_count", dependencies=[authentication()], description="获取接收消息数量"
)
async def _(bot_id: str) -> Result:
async def _(bot_id: str | None = None) -> 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)
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 ChatHistory.filter(
bot_id=bot_id, create_time__gte=now - timedelta(days=7)
week_count = await query.filter(
create_time__gte=now - timedelta(days=7, hours=now.hour, minutes=now.minute)
).count()
month_count = await ChatHistory.filter(
bot_id=bot_id, create_time__gte=now - timedelta(days=30)
month_count = await query.filter(
create_time__gte=now - timedelta(days=30, hours=now.hour, minutes=now.minute)
).count()
year_count = await ChatHistory.filter(
bot_id=bot_id, create_time__gte=now - timedelta(days=365)
year_count = await query.filter(
create_time__gte=now - timedelta(days=365, hours=now.hour, minutes=now.minute)
).count()
return Result.ok(
ChatHistoryCount(
QueryCount(
num=all_count,
day=day_count,
week=week_count,
month=month_count,
year=year_count,
)
)
@router.get(
"/get_all_call_count", dependencies=[authentication()], description="获取调用次数"
)
async def _(bot_id: str | None = None) -> Result:
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,
@ -182,11 +216,21 @@ async def _(bot_id: str) -> Result:
return Result.warning_("无Bot连接...")
@router.get("/get_nb_data", dependencies=[authentication()], description="获取nb数据")
async def _() -> Result:
return Result.ok(NonebotData(config=driver.config, run_time=int(run_time)))
@router.get("/get_nb_config", dependencies=[authentication()], description="获取nb配置")
async def _() -> Result:
return Result.ok(driver.config)
@router.get(
"/get_run_time", dependencies=[authentication()], description="获取nb运行时间"
)
async def _() -> Result:
return Result.ok(int(time.time() - run_time))
return Result.ok(int(run_time))
@router.get(

View File

@ -1,8 +1,8 @@
import time
import nonebot
from nonebot.adapters.onebot.v11 import Bot
from nonebot.drivers import Driver
from nonebot.adapters.onebot.v11 import Bot
driver: Driver = nonebot.get_driver()

View File

@ -1,8 +1,6 @@
from datetime import datetime
from pydantic import BaseModel
from nonebot.adapters import Bot
from nonebot.config import Config
from pydantic import BaseModel
class SystemStatus(BaseModel):
@ -36,7 +34,7 @@ class BaseInfo(BaseModel):
"""今日 累计接收消息"""
connect_time: int = 0
"""连接时间"""
connect_date: datetime | None = None
connect_date: str | None = None
"""连接日期"""
plugin_count: int = 0
@ -60,7 +58,7 @@ class BaseInfo(BaseModel):
arbitrary_types_allowed = True
class ChatHistoryCount(BaseModel):
class QueryCount(BaseModel):
"""
聊天记录数量
"""
@ -103,3 +101,10 @@ class HotPlugin(BaseModel):
"""插件名称"""
count: int
"""调用次数"""
class NonebotData(BaseModel):
config: Config
"""nb配置"""
run_time: int
"""运行时间"""

View File

@ -1,6 +1,8 @@
from nonebot import require
from fastapi import APIRouter
from zhenxun.models.plugin_info import PluginInfo
from .model import PluginIr
from ....base_model import Result
from ....utils import authentication
@ -19,7 +21,14 @@ router = APIRouter(prefix="/store")
async def _() -> Result:
try:
data = await ShopManage.get_data()
return Result.ok(data)
plugin_list = [
{**data[name].dict(), "name": name, "id": idx}
for idx, name in enumerate(data)
]
modules = await PluginInfo.filter(load_status=True).values_list(
"module", flat=True
)
return Result.ok({"install_module": modules, "plugin_list": plugin_list})
except Exception as e:
return Result.fail(f"获取插件商店插件信息失败: {type(e)}: {e}")
@ -32,11 +41,24 @@ async def _() -> Result:
async def _(param: PluginIr) -> Result:
try:
result = await ShopManage.add_plugin(param.id) # type: ignore
return Result.ok(result)
return Result.ok(info=result)
except Exception as e:
return Result.fail(f"安装插件失败: {type(e)}: {e}")
@router.post(
"/update_plugin",
dependencies=[authentication()],
deprecated="更新插件", # type: ignore
)
async def _(param: PluginIr) -> Result:
try:
result = await ShopManage.update_plugin(param.id) # type: ignore
return Result.ok(info=result)
except Exception as e:
return Result.fail(f"更新插件失败: {type(e)}: {e}")
@router.post(
"/remove_plugin",
dependencies=[authentication()],
@ -45,6 +67,6 @@ async def _(param: PluginIr) -> Result:
async def _(param: PluginIr) -> Result:
try:
result = await ShopManage.remove_plugin(param.id) # type: ignore
return Result.ok(result)
return Result.ok(info=result)
except Exception as e:
return Result.fail(f"移除插件失败: {type(e)}: {e}")

View File

@ -0,0 +1,22 @@
from tortoise import fields
from zhenxun.services.db_context import Model
class BotConnectLog(Model):
id = fields.IntField(pk=True, generated=True, auto_increment=True)
"""自增id"""
bot_id = fields.CharField(255, description="Bot id")
"""Bot id"""
platform = fields.CharField(255, null=True, description="平台")
"""平台"""
connect_time = fields.DatetimeField(description="连接时间")
"""日期"""
type = fields.IntField(null=True, description="1: 连接, 0: 断开")
"""1: 连接, 0: 断开"""
create_time = fields.DatetimeField(auto_now_add=True)
"""创建时间"""
class Meta: # type: ignore
table = "bot_connect_log"
table_description = "bot连接表"

View File

@ -4,7 +4,6 @@ from zhenxun.services.db_context import Model
class Statistics(Model):
id = fields.IntField(pk=True, generated=True, auto_increment=True)
"""自增id"""
user_id = fields.CharField(255)
@ -15,15 +14,19 @@ class Statistics(Model):
"""插件名称"""
create_time = fields.DatetimeField(auto_now=True)
"""添加日期"""
bot_id = fields.CharField(255, null=True)
"""Bot Id"""
class Meta:
class Meta: # type: ignore
table = "statistics"
table_description = "插件调用统计数据库"
@classmethod
async def _run_script(cls):
return [
"ALTER TABLE statistics RENAME COLUMN user_qq TO user_id;", # 将user_qq改为user_id
"ALTER TABLE statistics RENAME COLUMN user_qq TO user_id;",
# 将user_qq改为user_id
"ALTER TABLE statistics ALTER COLUMN user_id TYPE character varying(255);",
"ALTER TABLE statistics ALTER COLUMN group_id TYPE character varying(255);",
"ALTER TABLE statistics ADD bot_id Text DEFAULT '';",
]

View File

@ -1,6 +1,6 @@
from collections.abc import Generator
from .consts import GITHUB_REPO_URL_PATTERN
from .const import GITHUB_REPO_URL_PATTERN
from .func import get_fastest_raw_formats, get_fastest_archive_formats
from .models import RepoAPI, RepoInfo, GitHubStrategy, JsdelivrStrategy

View File

@ -1,7 +1,7 @@
from aiocache import cached
from ..http_utils import AsyncHttpx
from .consts import (
from .const import (
ARCHIVE_URL_FORMAT,
RAW_CONTENT_FORMAT,
RELEASE_ASSETS_FORMAT,

View File

@ -5,7 +5,7 @@ from strenum import StrEnum
from pydantic import BaseModel
from ..http_utils import AsyncHttpx
from .consts import CACHED_API_TTL, GIT_API_TREES_FORMAT, JSD_PACKAGE_API_FORMAT
from .const import CACHED_API_TTL, GIT_API_TREES_FORMAT, JSD_PACKAGE_API_FORMAT
from .func import (
get_fastest_raw_formats,
get_fastest_archive_formats,
@ -199,13 +199,15 @@ class GitHubStrategy:
body: TreeInfo
def export_files(self, module_path: str) -> list[str]:
def export_files(self, module_path: str, is_dir: bool) -> list[str]:
"""导出文件路径"""
tree_info = self.body
return [
file.path
for file in tree_info.tree
if file.type == TreeType.FILE and file.path.startswith(module_path)
if file.type == TreeType.FILE
and file.path.startswith(module_path)
and (not is_dir or file.path[len(module_path)] == "/")
]
@classmethod
@ -229,4 +231,4 @@ class GitHubStrategy:
def get_files(self, module_path: str, is_dir: bool = True) -> list[str]:
"""获取文件路径"""
return self.export_files(module_path)
return self.export_files(module_path, is_dir)

View File

@ -2,7 +2,6 @@ from pydantic import BaseModel
class CommonSql(BaseModel):
sql: str
"""sql语句"""
remark: str