From d3a4a5bbf7502a7a987ecd8cfdc98b25a7a2bc84 Mon Sep 17 00:00:00 2001 From: AkashiCoin Date: Wed, 2 Oct 2024 12:54:12 +0800 Subject: [PATCH 1/4] =?UTF-8?q?:bug:=20fix(github=5Futils):=20=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E8=B7=AF=E5=BE=84=E8=A7=A3=E6=9E=90=E9=97=AE=E9=A2=98?= =?UTF-8?q?=20(#1668)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * :bug: fix(github_utils): 修复路径解析问题 * chore(version): Update version to v0.2.3-a39d7a3 --------- Co-authored-by: AkashiCoin --- __version__ | 2 +- zhenxun/utils/github_utils/__init__.py | 2 +- zhenxun/utils/github_utils/{consts.py => const.py} | 0 zhenxun/utils/github_utils/func.py | 2 +- zhenxun/utils/github_utils/models.py | 10 ++++++---- 5 files changed, 9 insertions(+), 7 deletions(-) rename zhenxun/utils/github_utils/{consts.py => const.py} (100%) diff --git a/__version__ b/__version__ index 6a49e381..a71fac09 100644 --- a/__version__ +++ b/__version__ @@ -1 +1 @@ -__version__: v0.2.3 +__version__: v0.2.3-a39d7a3 diff --git a/zhenxun/utils/github_utils/__init__.py b/zhenxun/utils/github_utils/__init__.py index 89b0a80a..eb1b047f 100644 --- a/zhenxun/utils/github_utils/__init__.py +++ b/zhenxun/utils/github_utils/__init__.py @@ -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 diff --git a/zhenxun/utils/github_utils/consts.py b/zhenxun/utils/github_utils/const.py similarity index 100% rename from zhenxun/utils/github_utils/consts.py rename to zhenxun/utils/github_utils/const.py diff --git a/zhenxun/utils/github_utils/func.py b/zhenxun/utils/github_utils/func.py index 95d2a3ef..03c43182 100644 --- a/zhenxun/utils/github_utils/func.py +++ b/zhenxun/utils/github_utils/func.py @@ -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, diff --git a/zhenxun/utils/github_utils/models.py b/zhenxun/utils/github_utils/models.py index 17089281..a7bbad06 100644 --- a/zhenxun/utils/github_utils/models.py +++ b/zhenxun/utils/github_utils/models.py @@ -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) From 42b6e94564b55816f3e271702bf46b86391d908b Mon Sep 17 00:00:00 2001 From: moelanp <104612722+molanp@users.noreply.github.com> Date: Wed, 2 Oct 2024 18:32:21 +0800 Subject: [PATCH 2/4] =?UTF-8?q?:sparkles:=20=E6=9B=B4=E6=96=B0=E6=8F=92?= =?UTF-8?q?=E4=BB=B6=E5=95=86=E5=BA=97=E5=8A=9F=E8=83=BD=EF=BC=8C=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E9=80=9A=E8=BF=87=E6=A8=A1=E5=9D=97=E5=90=8D=E6=93=8D?= =?UTF-8?q?=E4=BD=9C=E6=8F=92=E4=BB=B6(#1670)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 更新插件商店功能,支持通过模块名操作插件 - 扩展插件添加、移除和更新功能,支持使用插件ID或模块名 - 增加更新全部插件的功能 - 优化插件商店的命令使用说明 - 修复了一些与插件模块名相关的逻辑问题 * 优化插件更新和加载机制,提供测试函数 - 修复了插件更新函数中的条件判断逻辑 * 优化插件更新通知的格式 调整了插件更新通知的文本格式,去掉了多余的换行符,使消息内容更加紧凑和清晰。 * 更新测试用例中的消息格式,将插件更新通知中的空格改为换行符 * 移除版本号更新 * 重构插件管理器的数据源解析逻辑 - 将插件ID和模块名的检查逻辑移至单独的私有方法 _resolve_plugin_key - 简化了 get_info 和 update_plugin 方法中的逻辑 - 提高了代码的可读性和可维护性 * 优化插件商店数据源类的插件查询逻辑 简化了ShopManage类中查询插件信息的逻辑。通过新增的_resolve_plugin_key类方法来解析插件ID或模块名,如果解析失败则捕获ValueError异常并返回错误信息。这样可以更清晰地处理插件查询逻辑,并避免冗余代码。 * 移除更新全部插件日志中的f-string 更新全部插件功能中,移除了日志记录中的f-string,简化了日志消息的格式。这个更改可能是为了统一日志记录的风格或者减少不必要的字符串格式化操作。 * Revert "移除版本号更新" This reverts commit 2bcaa6f12e61136dc6bf2ca5b9fde29583a50c12. --------- Co-authored-by: molanp Co-authored-by: HibiKier <45528451+HibiKier@users.noreply.github.com> Co-authored-by: AkashiCoin --- __version__ | 2 +- .../plugin_store/test_update_all_plugin.py | 120 ++++++++++++++++++ .../builtin_plugins/plugin_store/__init__.py | 50 ++++++-- .../plugin_store/data_source.py | 86 ++++++++++--- 4 files changed, 231 insertions(+), 27 deletions(-) create mode 100644 tests/builtin_plugins/plugin_store/test_update_all_plugin.py diff --git a/__version__ b/__version__ index a71fac09..4f3304af 100644 --- a/__version__ +++ b/__version__ @@ -1 +1 @@ -__version__: v0.2.3-a39d7a3 +__version__: v0.2.3-ea00860 diff --git a/tests/builtin_plugins/plugin_store/test_update_all_plugin.py b/tests/builtin_plugins/plugin_store/test_update_all_plugin.py new file mode 100644 index 00000000..d9205765 --- /dev/null +++ b/tests/builtin_plugins/plugin_store/test_update_all_plugin.py @@ -0,0 +1,120 @@ +from typing import cast +from pathlib import Path +from collections.abc import Callable + +from nonebug import App +from respx import MockRouter +from pytest_mock import MockerFixture +from nonebot.adapters.onebot.v11 import Bot +from nonebot.adapters.onebot.v11.message import Message +from nonebot.adapters.onebot.v11.event import GroupMessageEvent + +from tests.utils import _v11_group_message_event +from tests.config import BotId, UserId, GroupId, MessageId +from tests.builtin_plugins.plugin_store.utils import init_mocked_api + + +async def test_update_all_plugin_basic_need_update( + app: App, + mocker: MockerFixture, + mocked_api: MockRouter, + create_bot: Callable, + tmp_path: Path, +) -> None: + """ + 测试更新基础插件,插件需要更新 + """ + from zhenxun.builtin_plugins.plugin_store import _matcher + + init_mocked_api(mocked_api=mocked_api) + mock_base_path = mocker.patch( + "zhenxun.builtin_plugins.plugin_store.data_source.BASE_PATH", + new=tmp_path / "zhenxun", + ) + mocker.patch( + "zhenxun.builtin_plugins.plugin_store.data_source.ShopManage.get_loaded_plugins", + return_value=[("search_image", "0.0")], + ) + + + async with app.test_matcher(_matcher) as ctx: + bot = create_bot(ctx) + bot: Bot = cast(Bot, bot) + raw_message = "更新全部插件" + event: GroupMessageEvent = _v11_group_message_event( + message=raw_message, + self_id=BotId.QQ_BOT, + user_id=UserId.SUPERUSER, + group_id=GroupId.GROUP_ID_LEVEL_5, + message_id=MessageId.MESSAGE_ID, + to_me=True, + ) + ctx.receive_event(bot=bot, event=event) + ctx.should_call_send( + event=event, + message=Message(message="正在更新全部插件"), + result=None, + bot=bot, + ) + ctx.should_call_send( + event=event, + message=Message(message="已更新插件 识图\n共计1个插件! 重启后生效"), + result=None, + bot=bot, + ) + assert mocked_api["basic_plugins"].called + assert mocked_api["extra_plugins"].called + assert mocked_api["search_image_plugin_file_init"].called + assert (mock_base_path / "plugins" / "search_image" / "__init__.py").is_file() + + +async def test_update_all_plugin_basic_is_new( + app: App, + mocker: MockerFixture, + mocked_api: MockRouter, + create_bot: Callable, + tmp_path: Path, +) -> None: + """ + 测试更新基础插件,插件是最新版 + """ + from zhenxun.builtin_plugins.plugin_store import _matcher + + init_mocked_api(mocked_api=mocked_api) + mocker.patch( + "zhenxun.builtin_plugins.plugin_store.data_source.BASE_PATH", + new=tmp_path / "zhenxun", + ) + mocker.patch( + "zhenxun.builtin_plugins.plugin_store.data_source.ShopManage.get_loaded_plugins", + return_value=[("search_image", "0.1")], + ) + + + async with app.test_matcher(_matcher) as ctx: + bot = create_bot(ctx) + bot: Bot = cast(Bot, bot) + raw_message = "更新全部插件" + event: GroupMessageEvent = _v11_group_message_event( + message=raw_message, + self_id=BotId.QQ_BOT, + user_id=UserId.SUPERUSER, + group_id=GroupId.GROUP_ID_LEVEL_5, + message_id=MessageId.MESSAGE_ID, + to_me=True, + ) + ctx.receive_event(bot=bot, event=event) + ctx.should_call_send( + event=event, + message=Message(message="正在更新全部插件"), + result=None, + bot=bot, + ) + ctx.should_call_send( + event=event, + message=Message(message="全部插件已是最新版本"), + result=None, + bot=bot, + ) + assert mocked_api["basic_plugins"].called + assert mocked_api["extra_plugins"].called diff --git a/zhenxun/builtin_plugins/plugin_store/__init__.py b/zhenxun/builtin_plugins/plugin_store/__init__.py index 4ff8c940..06e0b481 100644 --- a/zhenxun/builtin_plugins/plugin_store/__init__.py +++ b/zhenxun/builtin_plugins/plugin_store/__init__.py @@ -15,10 +15,11 @@ __plugin_meta__ = PluginMetadata( description="插件商店", usage=""" 插件商店 : 查看当前的插件商店 - 添加插件 id : 添加插件 - 移除插件 id : 移除插件 + 添加插件 id or module : 添加插件 + 移除插件 id or module : 移除插件 搜索插件 name or author : 搜索插件 - 更新插件 id : 更新插件 + 更新插件 id or module : 更新插件 + 更新全部插件 : 更新全部插件 """.strip(), extra=PluginExtraData( author="HibiKier", @@ -30,10 +31,11 @@ __plugin_meta__ = PluginMetadata( _matcher = on_alconna( Alconna( "插件商店", - Subcommand("add", Args["plugin_id", int]), - Subcommand("remove", Args["plugin_id", int]), + Subcommand("add", Args["plugin_id", int | str]), + Subcommand("remove", Args["plugin_id", int | str]), Subcommand("search", Args["plugin_name_or_author", str]), - Subcommand("update", Args["plugin_id", int]), + Subcommand("update", Args["plugin_id", int | str]), + Subcommand("update_all"), ), permission=SUPERUSER, priority=1, @@ -68,6 +70,13 @@ _matcher.shortcut( prefix=True, ) +_matcher.shortcut( + r"更新全部插件", + command="插件商店", + arguments=["update_all"], + prefix=True, +) + @_matcher.assign("$main") async def _(session: EventSession): @@ -81,9 +90,12 @@ async def _(session: EventSession): @_matcher.assign("add") -async def _(session: EventSession, plugin_id: int): +async def _(session: EventSession, plugin_id: int | str): try: - await MessageUtils.build_message(f"正在添加插件 Id: {plugin_id}").send() + if isinstance(plugin_id, str): + await MessageUtils.build_message(f"正在添加插件 Module: {plugin_id}").send() + else: + await MessageUtils.build_message(f"正在添加插件 Id: {plugin_id}").send() result = await ShopManage.add_plugin(plugin_id) except Exception as e: logger.error(f"添加插件 Id: {plugin_id}失败", "插件商店", session=session, e=e) @@ -95,7 +107,7 @@ async def _(session: EventSession, plugin_id: int): @_matcher.assign("remove") -async def _(session: EventSession, plugin_id: int): +async def _(session: EventSession, plugin_id: int | str): try: result = await ShopManage.remove_plugin(plugin_id) except Exception as e: @@ -126,9 +138,12 @@ async def _(session: EventSession, plugin_name_or_author: str): @_matcher.assign("update") -async def _(session: EventSession, plugin_id: int): +async def _(session: EventSession, plugin_id: int | str): try: - await MessageUtils.build_message(f"正在更新插件 Id: {plugin_id}").send() + if isinstance(plugin_id, str): + await MessageUtils.build_message(f"正在更新插件 Module: {plugin_id}").send() + else: + await MessageUtils.build_message(f"正在更新插件 Id: {plugin_id}").send() result = await ShopManage.update_plugin(plugin_id) except Exception as e: logger.error(f"更新插件 Id: {plugin_id}失败", "插件商店", session=session, e=e) @@ -137,3 +152,16 @@ async def _(session: EventSession, plugin_id: int): ).finish() logger.info(f"更新插件 Id: {plugin_id}", "插件商店", session=session) await MessageUtils.build_message(result).send() + +@_matcher.assign("update_all") +async def _(session: EventSession): + try: + await MessageUtils.build_message("正在更新全部插件").send() + result = await ShopManage.update_all_plugin() + except Exception as e: + logger.error("更新全部插件失败", "插件商店", session=session, e=e) + await MessageUtils.build_message( + f"更新全部插件失败 e: {e}" + ).finish() + logger.info("更新全部插件", "插件商店", session=session) + await MessageUtils.build_message(result).send() \ No newline at end of file diff --git a/zhenxun/builtin_plugins/plugin_store/data_source.py b/zhenxun/builtin_plugins/plugin_store/data_source.py index 26c3edff..d63309b8 100644 --- a/zhenxun/builtin_plugins/plugin_store/data_source.py +++ b/zhenxun/builtin_plugins/plugin_store/data_source.py @@ -175,19 +175,20 @@ class ShopManage: ) @classmethod - async def add_plugin(cls, plugin_id: int) -> str: + async def add_plugin(cls, plugin_id: int | str) -> str: """添加插件 参数: - plugin_id: 插件id + plugin_id: 插件id或模块名 返回: str: 返回消息 """ data: dict[str, StorePluginInfo] = await cls.get_data() - if plugin_id < 0 or plugin_id >= len(data): - return "插件ID不存在..." - plugin_key = list(data.keys())[plugin_id] + try: + plugin_key = await cls._resolve_plugin_key(plugin_id) + except ValueError as e: + return str(e) plugin_list = await cls.get_loaded_plugins("module") plugin_info = data[plugin_key] if plugin_info.module in [p[0] for p in plugin_list]: @@ -265,20 +266,21 @@ class ShopManage: raise Exception("插件下载失败") @classmethod - async def remove_plugin(cls, plugin_id: int) -> str: + async def remove_plugin(cls, plugin_id: int | str) -> str: """移除插件 参数: - plugin_id: 插件id + plugin_id: 插件id或模块名 返回: str: 返回消息 """ data: dict[str, StorePluginInfo] = await cls.get_data() - if plugin_id < 0 or plugin_id >= len(data): - return "插件ID不存在..." - plugin_key = list(data.keys())[plugin_id] - plugin_info = data[plugin_key] # type: ignore + try: + plugin_key = await cls._resolve_plugin_key(plugin_id) + except ValueError as e: + return str(e) + plugin_info = data[plugin_key] path = BASE_PATH if plugin_info.github_url: path = BASE_PATH / "plugins" @@ -340,7 +342,7 @@ class ShopManage: ) @classmethod - async def update_plugin(cls, plugin_id: int) -> str: + async def update_plugin(cls, plugin_id: int | str) -> str: """更新插件 参数: @@ -350,9 +352,10 @@ class ShopManage: str: 返回消息 """ data: dict[str, StorePluginInfo] = await cls.get_data() - if plugin_id < 0 or plugin_id >= len(data): - return "插件ID不存在..." - plugin_key = list(data.keys())[plugin_id] + try: + plugin_key = await cls._resolve_plugin_key(plugin_id) + except ValueError as e: + return str(e) logger.info(f"尝试更新插件 {plugin_key}", "插件管理") plugin_info = data[plugin_key] plugin_list = await cls.get_loaded_plugins("module", "version") @@ -373,3 +376,56 @@ class ShopManage: is_external, ) return f"插件 {plugin_key} 更新成功! 重启后生效" + + @classmethod + async def update_all_plugin(cls) -> str: + """更新插件 + + 参数: + plugin_id: 插件id + + 返回: + str: 返回消息 + """ + data: dict[str, StorePluginInfo] = await cls.get_data() + plugin_list = list(data.keys()) + update_list = [] + logger.info(f"尝试更新全部插件 {plugin_list}", "插件管理") + for plugin_key in plugin_list: + plugin_info = data[plugin_key] + plugin_list = await cls.get_loaded_plugins("module", "version") + suc_plugin = {p[0]: (p[1] or "Unknown") for p in plugin_list} + if plugin_info.module not in [p[0] for p in plugin_list]: + logger.debug(f"插件 {plugin_key} 未安装,跳过", "插件管理") + continue + if cls.check_version_is_new(plugin_info, suc_plugin): + logger.debug(f"插件 {plugin_key} 已是最新版本,跳过", "插件管理") + continue + logger.info(f"正在更新插件 {plugin_key}", "插件管理") + is_external = True + if plugin_info.github_url is None: + plugin_info.github_url = DEFAULT_GITHUB_URL + is_external = False + await cls.install_plugin_with_repo( + plugin_info.github_url, + plugin_info.module_path, + plugin_info.is_dir, + is_external, + ) + update_list.append(plugin_key) + if len(update_list) == 0: + return "全部插件已是最新版本" + return "已更新插件 {}\n共计{}个插件! 重启后生效".format( + "\n- ".join(update_list), len(update_list) + ) + @classmethod + async def _resolve_plugin_key(cls, plugin_id: int | str) -> str: + data: dict[str, StorePluginInfo] = await cls.get_data() + if isinstance(plugin_id, int): + if plugin_id < 0 or plugin_id >= len(data): + raise ValueError("插件ID不存在...") + return list(data.keys())[plugin_id] + elif isinstance(plugin_id, str): + if plugin_id not in [v.module for k, v in data.items()]: + raise ValueError("插件Module不存在...") + return {v.module: k for k, v in data.items()}[plugin_id] From f08114f6c519760f6d76eb5286140a772f9b532b Mon Sep 17 00:00:00 2001 From: HibiKier <45528451+HibiKier@users.noreply.github.com> Date: Thu, 3 Oct 2024 23:56:17 +0800 Subject: [PATCH 3/4] =?UTF-8?q?=E2=9C=A8=20=E6=8F=90=E4=BE=9B=E6=96=B0webu?= =?UTF-8?q?i=20api=20(#1673)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zhenxun/builtin_plugins/__init__.py | 56 ++++--- .../statistics/statistics_hook.py | 18 +-- .../web_ui/api/tabs/dashboard/__init__.py | 150 +++++++++++++++++- .../web_ui/api/tabs/dashboard/data_source.py | 5 +- .../web_ui/api/tabs/dashboard/model.py | 51 +++++- .../web_ui/api/tabs/main/__init__.py | 78 +++++++-- .../web_ui/api/tabs/main/data_source.py | 2 +- .../web_ui/api/tabs/main/model.py | 15 +- .../web_ui/api/tabs/plugin_manage/store.py | 28 +++- zhenxun/models/bot_connect_log.py | 22 +++ zhenxun/models/statistics.py | 9 +- zhenxun/utils/plugin_models/base.py | 1 - 12 files changed, 368 insertions(+), 67 deletions(-) create mode 100644 zhenxun/models/bot_connect_log.py diff --git a/zhenxun/builtin_plugins/__init__.py b/zhenxun/builtin_plugins/__init__.py index 9bce3c1c..acf3764e 100644 --- a/zhenxun/builtin_plugins/__init__.py +++ b/zhenxun/builtin_plugins/__init__.py @@ -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,16 +24,30 @@ 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 ( + join ( select user_id, max(t2.impression) as max_impression from public.sign_group_users t2 group by user_id @@ -38,7 +57,7 @@ from public.sign_group_users t1 BAG_SQL = """ select t1.user_id, t1.gold, t1.property from public.bag_users t1 - join ( + join ( select user_id, max(t2.gold) as max_gold from public.bag_users t2 group by user_id @@ -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"], diff --git a/zhenxun/builtin_plugins/statistics/statistics_hook.py b/zhenxun/builtin_plugins/statistics/statistics_hook.py index d1fc4635..7b352bcb 100644 --- a/zhenxun/builtin_plugins/statistics/statistics_hook.py +++ b/zhenxun/builtin_plugins/statistics/statistics_hook.py @@ -1,16 +1,17 @@ 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.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="功能调用统计", @@ -36,13 +37,12 @@ async def _( if session.id1: plugin = await PluginInfo.get_or_none(module=matcher.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", - ]: + if plugin_type == PluginType.NORMAL: + logger.debug(f"提交调用记录: {matcher.plugin_name}...", session=session) await Statistics.create( 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, ) diff --git a/zhenxun/builtin_plugins/web_ui/api/tabs/dashboard/__init__.py b/zhenxun/builtin_plugins/web_ui/api/tabs/dashboard/__init__.py index 60159296..fce48089 100644 --- a/zhenxun/builtin_plugins/web_ui/api/tabs/dashboard/__init__.py +++ b/zhenxun/builtin_plugins/web_ui/api/tabs/dashboard/__init__.py @@ -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)) diff --git a/zhenxun/builtin_plugins/web_ui/api/tabs/dashboard/data_source.py b/zhenxun/builtin_plugins/web_ui/api/tabs/dashboard/data_source.py index b09d4ba9..4c39fb64 100644 --- a/zhenxun/builtin_plugins/web_ui/api/tabs/dashboard/data_source.py +++ b/zhenxun/builtin_plugins/web_ui/api/tabs/dashboard/data_source.py @@ -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 diff --git a/zhenxun/builtin_plugins/web_ui/api/tabs/dashboard/model.py b/zhenxun/builtin_plugins/web_ui/api/tabs/dashboard/model.py index d2891251..1293c7d7 100644 --- a/zhenxun/builtin_plugins/web_ui/api/tabs/dashboard/model.py +++ b/zhenxun/builtin_plugins/web_ui/api/tabs/dashboard/model.py @@ -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 + """一年内调用次数""" diff --git a/zhenxun/builtin_plugins/web_ui/api/tabs/main/__init__.py b/zhenxun/builtin_plugins/web_ui/api/tabs/main/__init__.py index 88f5ee9b..8a454c61 100644 --- a/zhenxun/builtin_plugins/web_ui/api/tabs/main/__init__.py +++ b/zhenxun/builtin_plugins/web_ui/api/tabs/main/__init__.py @@ -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( diff --git a/zhenxun/builtin_plugins/web_ui/api/tabs/main/data_source.py b/zhenxun/builtin_plugins/web_ui/api/tabs/main/data_source.py index ca445016..281ac701 100644 --- a/zhenxun/builtin_plugins/web_ui/api/tabs/main/data_source.py +++ b/zhenxun/builtin_plugins/web_ui/api/tabs/main/data_source.py @@ -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() diff --git a/zhenxun/builtin_plugins/web_ui/api/tabs/main/model.py b/zhenxun/builtin_plugins/web_ui/api/tabs/main/model.py index c9d76706..d8ec0c0f 100644 --- a/zhenxun/builtin_plugins/web_ui/api/tabs/main/model.py +++ b/zhenxun/builtin_plugins/web_ui/api/tabs/main/model.py @@ -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 + """运行时间""" diff --git a/zhenxun/builtin_plugins/web_ui/api/tabs/plugin_manage/store.py b/zhenxun/builtin_plugins/web_ui/api/tabs/plugin_manage/store.py index f2b54859..aa6584ab 100644 --- a/zhenxun/builtin_plugins/web_ui/api/tabs/plugin_manage/store.py +++ b/zhenxun/builtin_plugins/web_ui/api/tabs/plugin_manage/store.py @@ -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}") diff --git a/zhenxun/models/bot_connect_log.py b/zhenxun/models/bot_connect_log.py new file mode 100644 index 00000000..768f049c --- /dev/null +++ b/zhenxun/models/bot_connect_log.py @@ -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连接表" diff --git a/zhenxun/models/statistics.py b/zhenxun/models/statistics.py index 43576fcb..100e01e9 100644 --- a/zhenxun/models/statistics.py +++ b/zhenxun/models/statistics.py @@ -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 boe_id Text DEFAULT '';", ] diff --git a/zhenxun/utils/plugin_models/base.py b/zhenxun/utils/plugin_models/base.py index a50f6b4a..74692004 100644 --- a/zhenxun/utils/plugin_models/base.py +++ b/zhenxun/utils/plugin_models/base.py @@ -2,7 +2,6 @@ from pydantic import BaseModel class CommonSql(BaseModel): - sql: str """sql语句""" remark: str From bd50e17c5821a00b0466e1475e451cc07cab0623 Mon Sep 17 00:00:00 2001 From: HibiKier <45528451+HibiKier@users.noreply.github.com> Date: Fri, 4 Oct 2024 00:14:36 +0800 Subject: [PATCH 4/4] =?UTF-8?q?=F0=9F=90=9B=20=E4=BF=AE=E5=A4=8D=E8=B0=83?= =?UTF-8?q?=E7=94=A8=E7=BB=9F=E8=AE=A1=E5=BC=82=E5=B8=B8=20(#1674)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../statistics/statistics_hook.py | 36 ++++++++++++++----- zhenxun/models/statistics.py | 2 +- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/zhenxun/builtin_plugins/statistics/statistics_hook.py b/zhenxun/builtin_plugins/statistics/statistics_hook.py index 7b352bcb..9842ad09 100644 --- a/zhenxun/builtin_plugins/statistics/statistics_hook.py +++ b/zhenxun/builtin_plugins/statistics/statistics_hook.py @@ -5,6 +5,7 @@ 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 @@ -22,6 +23,8 @@ __plugin_meta__ = PluginMetadata( ).dict(), ) +TEMP_LIST = [] + @run_postprocessor async def _( @@ -34,15 +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: logger.debug(f"提交调用记录: {matcher.plugin_name}...", session=session) - await Statistics.create( - 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, + 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) diff --git a/zhenxun/models/statistics.py b/zhenxun/models/statistics.py index 100e01e9..ad29cb17 100644 --- a/zhenxun/models/statistics.py +++ b/zhenxun/models/statistics.py @@ -28,5 +28,5 @@ class Statistics(Model): # 将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 boe_id Text DEFAULT '';", + "ALTER TABLE statistics ADD bot_id Text DEFAULT '';", ]