From 1d6a8a464e1eb978187e1bca16e9d9623ffef70d Mon Sep 17 00:00:00 2001 From: liuwei Date: Thu, 27 Mar 2025 12:17:15 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=A0=E9=99=A4=E5=8E=86=E5=8F=B2=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugins/stats_dashboard/__init__.py | 7 - plugins/stats_dashboard/config.toml | 6 - plugins/stats_dashboard/dashboard_server.py | 371 -------------- plugins/stats_dashboard/main.py | 123 ----- plugins/stats_dashboard/static/favicon.ico | Bin 15406 -> 0 bytes plugins/stats_dashboard/templates/base.html | 186 ------- plugins/stats_dashboard/templates/errors.html | 146 ------ plugins/stats_dashboard/templates/groups.html | 79 --- plugins/stats_dashboard/templates/index.html | 386 -------------- .../stats_dashboard/templates/plugins.html | 255 ---------- .../templates/robot_management.html | 473 ------------------ plugins/stats_dashboard/templates/users.html | 77 --- 12 files changed, 2109 deletions(-) delete mode 100644 plugins/stats_dashboard/__init__.py delete mode 100644 plugins/stats_dashboard/config.toml delete mode 100644 plugins/stats_dashboard/dashboard_server.py delete mode 100644 plugins/stats_dashboard/main.py delete mode 100644 plugins/stats_dashboard/static/favicon.ico delete mode 100644 plugins/stats_dashboard/templates/base.html delete mode 100644 plugins/stats_dashboard/templates/errors.html delete mode 100644 plugins/stats_dashboard/templates/groups.html delete mode 100644 plugins/stats_dashboard/templates/index.html delete mode 100644 plugins/stats_dashboard/templates/plugins.html delete mode 100644 plugins/stats_dashboard/templates/robot_management.html delete mode 100644 plugins/stats_dashboard/templates/users.html diff --git a/plugins/stats_dashboard/__init__.py b/plugins/stats_dashboard/__init__.py deleted file mode 100644 index 9cb4659..0000000 --- a/plugins/stats_dashboard/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -from .main import StatsDashboardPlugin - -def get_plugin(): - """获取插件实例""" - return StatsDashboardPlugin() - -__all__ = ['StatsDashboardPlugin', 'get_plugin'] \ No newline at end of file diff --git a/plugins/stats_dashboard/config.toml b/plugins/stats_dashboard/config.toml deleted file mode 100644 index 35eae07..0000000 --- a/plugins/stats_dashboard/config.toml +++ /dev/null @@ -1,6 +0,0 @@ -enable = false -host = "0.0.0.0" -port = 8888 -username = "admin" -password = "admin123" -auto_start = "True" \ No newline at end of file diff --git a/plugins/stats_dashboard/dashboard_server.py b/plugins/stats_dashboard/dashboard_server.py deleted file mode 100644 index c24a86e..0000000 --- a/plugins/stats_dashboard/dashboard_server.py +++ /dev/null @@ -1,371 +0,0 @@ -import logging -import threading -import time -import os -from datetime import datetime - -from flask import Flask, render_template, request, jsonify, redirect, url_for, session, send_from_directory - -from db.connection import DBConnectionManager -from db.message_storage import MessageStorageDB -from db.stats_db import StatsDBOperator -from utils.wechat.contact_manager import ContactManager -from robot_cmd.robot_command import GroupBotManager, Feature, PermissionStatus - -class DashboardServer: - """统计看板服务器""" - - def __init__(self, host: str = "0.0.0.0", port: int = 8888, - username: str = "admin", password: str = "admin123"): - self.host = host - self.port = port - self.username = username - self.password = password - self.logger = logging.getLogger("DashboardServer") - - # 修正:使用单例模式获取数据库连接 - self.db_manager = DBConnectionManager.get_instance() - self.stats_db = StatsDBOperator(self.db_manager) - self.message_storage = MessageStorageDB(self.db_manager) - # 获取联系人管理器实例 - self.contact_manager = ContactManager.get_instance() - self.app = self._create_app() - self._stop_event = threading.Event() - self._server = None # 添加:存储服务器实例 - - def _create_app(self) -> Flask: - """创建Flask应用""" - app = Flask(__name__) - app.secret_key = "stats_dashboard_secret_key" - - # 添加:实现基本的身份验证 - def check_auth(): - auth = request.authorization - if not auth or auth.username != self.username or auth.password != self.password: - return False - return True - - # 静态文件目录 - static_folder = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'static') - - @app.route('/static/') - def serve_static(filename): - return send_from_directory(static_folder, filename) - - # 添加一个路由处理favicon请求 - @app.route('/favicon.ico') - def favicon(): - return send_from_directory(os.path.join(app.root_path, 'static'), - 'favicon.ico', mimetype='image/vnd.microsoft.icon') - @app.route('/') - def index(): - return render_template('index.html') - - @app.route('/plugins') - def plugins(): - return render_template('plugins.html') - - @app.route('/users') - def users_page(): - return render_template('users.html') - - @app.route('/groups') - def groups(): - return render_template('groups.html') - - @app.route('/errors') - def errors(): - return render_template('errors.html') - - # 在_create_app方法中添加新的路由 - @app.route('/robot_management') - def robot_management(): - return render_template('robot_management.html') - - @app.route('/api/robot/groups') - def api_robot_groups(): - # 获取所有群组列表 - - groups = GroupBotManager.get_group_list() - group_data = [] - - for group_id in groups: - group_name = self.contact_manager.get_nickname(group_id) - robot_status = GroupBotManager.get_group_permission(group_id, Feature.ROBOT) - group_data.append({ - "group_id": group_id, - "group_name": group_name, - "robot_status": robot_status.value - }) - - return jsonify({"success": True, "data": group_data}) - - @app.route('/api/robot/group//permissions') - def api_robot_group_permissions(group_id): - - permissions = GroupBotManager.list_group_permissions(group_id) - permission_data = [] - - for feature, status in permissions.items(): - permission_data.append({ - "feature_id": feature.value, - "feature_name": feature.name, - "feature_description": feature.description, - "status": status.value - }) - - return jsonify({"success": True, "data": permission_data}) - - @app.route('/api/robot/group//permissions', methods=['POST']) - def api_update_robot_permissions(group_id): - # 更新群组功能权限 - - data = request.json - feature_id = data.get('feature_id') - status = data.get('status') - - try: - feature = Feature(int(feature_id)) - new_status = PermissionStatus(status) - - # 特殊处理ROBOT功能 - if feature == Feature.ROBOT: - r = self.db_manager.get_redis_connection() - if new_status == PermissionStatus.ENABLED: - GroupBotManager.local_cache["group_list"].add(group_id) - r.sadd("group:list", group_id) - else: - GroupBotManager.local_cache["group_list"].remove(group_id) - r.srem("group:list", group_id) - - GroupBotManager.set_group_permission(group_id, feature, new_status) - return jsonify({"success": True}) - except Exception as e: - self.logger.error(f"更新群组权限失败: {e}") - return jsonify({"success": False, "error": str(e)}), 400 - - @app.route('/api/robot/batch_operation', methods=['POST']) - def api_robot_batch_operation(): - # 批量操作接口 - data = request.json - operation = data.get('operation') - group_ids = data.get('group_ids', []) - - results = {} - - try: - if operation == 'remove_groups': - for group_id in group_ids: - result = GroupBotManager.remove_group(group_id) - results[group_id] = result - - return jsonify({"success": True, "results": results}) - else: - return jsonify({"success": False, "error": "不支持的操作类型"}), 400 - except Exception as e: - self.logger.error(f"批量操作失败: {e}") - return jsonify({"success": False, "error": str(e)}), 400 - - # 添加:手动添加群组的API接口 - @app.route('/api/robot/add_group', methods=['POST']) - def api_add_group(): - try: - data = request.json - group_id = data.get('group_id') - - if not group_id or not group_id.strip(): - return jsonify({"success": False, "error": "群组ID不能为空"}), 400 - - group_id = group_id.strip() - - # 检查群组是否已存在 - if group_id in GroupBotManager.local_cache["group_list"]: - return jsonify({"success": False, "error": "该群组已存在"}), 400 - - # 添加群组到列表并启用机器人功能 - GroupBotManager.local_cache["group_list"].add(group_id) - r = self.db_manager.get_redis_connection() - r.sadd("group:list", group_id) - - # 设置ROBOT功能为启用状态 - GroupBotManager.set_group_permission(group_id, Feature.ROBOT, PermissionStatus.ENABLED) - - # 获取群组名称(如果可能) - group_name = self.contact_manager.get_nickname(group_id) - - return jsonify({ - "success": True, - "message": f"群组 {group_id} 已成功添加", - "group": { - "group_id": group_id, - "group_name": group_name, - "robot_status": "enabled" - } - }) - except Exception as e: - self.logger.error(f"添加群组失败: {e}") - return jsonify({"success": False, "error": str(e)}), 500 - - @app.route('/api/plugin_stats') - def api_plugin_stats(): - days = request.args.get('days', 7, type=int) - stats = self.stats_db.get_plugin_stats(days) - return jsonify({"success": True, "data": stats}) - - @app.route('/api/user_stats') - def api_user_stats(): - days = request.args.get('days', 7, type=int) - limit = request.args.get('limit', 10, type=int) - stats = self.stats_db.get_user_stats(days, limit) - - # 将用户ID转换为名称 - for item in stats: - if 'user_id' in item: - user_id = item['user_id'] - item['user_name'] = self.contact_manager.get_nickname(user_id) - - return jsonify({"success": True, "data": stats}) - - @app.route('/api/group_stats') - def api_group_stats(): - days = request.args.get('days', 7, type=int) - limit = request.args.get('limit', 10, type=int) - stats = self.stats_db.get_group_stats(days, limit) - - # 将群ID转换为名称 - for item in stats: - if 'group_id' in item: - group_id = item['group_id'] - item['group_name'] = self.contact_manager.get_nickname(group_id) - - return jsonify({"success": True, "data": stats}) - - @app.route('/api/error_logs') - def api_error_logs(): - days = request.args.get('days', 7, type=int) - limit = request.args.get('limit', 50, type=int) - logs = self.stats_db.get_error_logs(days, limit) - return jsonify({"success": True, "data": logs}) - - @app.route('/api/error_detail/') - def api_error_detail(error_id): - detail = self.stats_db.get_error_detail(error_id) - return jsonify({"success": True, "data": detail}) - - # 修改:添加错误处理的API路由示例 - @app.route('/api/dashboard_summary') - def api_dashboard_summary(): - try: - days = request.args.get('days', 7, type=int) - summary = self.stats_db.get_dashboard_summary(days) - - # 转换用户和群组ID为名称 - if 'top_users' in summary: - for user in summary['top_users']: - if 'user_id' in user: - user['user_name'] = self.contact_manager.get_nickname(user['user_id']) - - if 'top_groups' in summary: - for group in summary['top_groups']: - if 'group_id' in group: - group['group_name'] = self.contact_manager.get_nickname(group['group_id']) - - self.logger.info(f"看板主页统计数据: {summary}") - return jsonify({"success": True, "data": summary}) - except Exception as e: - self.logger.error(f"获取仪表盘摘要数据出错: {e}") - return jsonify({"success": False, "error": str(e)}), 500 - - @app.route('/api/plugin_trend') - def api_plugin_trend(): - days = request.args.get('days', 7, type=int) - plugin_name = request.args.get('plugin_name', '') - trend = self.stats_db.get_plugin_trend(plugin_name, days) - - # 如果趋势数据中包含用户或群组ID,也进行转换 - if isinstance(trend, list): - for item in trend: - if 'user_id' in item: - item['user_name'] = self.contact_manager.get_nickname(item['user_id']) - if 'group_id' in item: - item['group_name'] = self.contact_manager.get_nickname(item['group_id']) - - self.logger.info(f"看板主页/api/plugin_trend: {trend}") - return jsonify({"success": True, "data": trend}) - - @app.route('/api/robot/group//message_trend', methods=['GET']) - def get_group_message_trend(group_id): - """获取群组消息趋势数据""" - try: - days = request.args.get('days', default=7, type=int) - # 获取消息存储实例 - trend_data = self.message_storage.get_message_trend(group_id, days) - - # 格式化数据为前端需要的格式 - dates = [] - counts = [] - for item in trend_data: - # 将日期转换为字符串 - if isinstance(item['date'], datetime): - date_str = item['date'].strftime('%Y-%m-%d') - else: - date_str = str(item['date']) - - dates.append(date_str) - counts.append(item['message_count']) - - return jsonify({ - 'success': True, - 'data': { - 'dates': dates, - 'counts': counts - } - }) - except Exception as e: - return jsonify({ - 'success': False, - 'error': str(e) - }) - - return app - - def run(self) -> None: - """运行服务器""" - try: - self.logger.info(f"启动统计看板服务器,地址: {self.host}:{self.port}") - # 修改:使用线程安全的方式运行服务器 - from werkzeug.serving import make_server - self._server = make_server(self.host, self.port, self.app) - self._server.serve_forever() - except Exception as e: - self.logger.error(f"运行统计看板服务器出错: {e}") - - def stop(self) -> None: - """停止服务器""" - try: - self.logger.info("正在停止统计看板服务器...") - self._stop_event.set() - - # 关闭服务器 - if self._server: - self._server.shutdown() - self._server = None - - # 等待所有线程完成 - time.sleep(0.5) # 给线程一些时间来完成 - - self.logger.info("统计看板服务器已完全停止") - except Exception as e: - self.logger.error(f"停止统计看板服务器出错: {e}") - raise - - - def __del__(self): - """析构函数,确保资源被释放""" - try: - if hasattr(self, '_server') and self._server: - self.stop() - except Exception as e: - if hasattr(self, 'logger'): - self.logger.error(f"DashboardServer 析构时出错: {e}") - diff --git a/plugins/stats_dashboard/main.py b/plugins/stats_dashboard/main.py deleted file mode 100644 index 4a1283d..0000000 --- a/plugins/stats_dashboard/main.py +++ /dev/null @@ -1,123 +0,0 @@ -import logging -import threading -from typing import Dict, Any, Tuple, Optional, List - -from plugin_common.plugin_interface import PluginInterface, PluginStatus -from .dashboard_server import DashboardServer - - -class StatsDashboardPlugin(PluginInterface): - """统计看板插件""" - - @property - def name(self) -> str: - return "统计看板" - - @property - def version(self) -> str: - return "1.0.0" - - @property - def description(self) -> str: - return "提供插件使用统计数据的可视化界面" - - @property - def author(self) -> str: - return "Trae AI" - - @property - def command_prefix(self) -> Optional[str]: - return "" # 不需要前缀,直接匹配命令 - - @property - def commands(self) -> List[str]: - return [] - - def __init__(self): - super().__init__() - self.logger = logging.getLogger("StatsDashboard") - self.LOG.info(f"正在初始化 {self.name} 插件...") - self.server = None - self.server_thread = None - - def initialize(self, config: Dict[str, Any]) -> bool: - - if not self._config["enable"]: - self.logger.info("统计看板插件已禁用") - return False - - # 创建看板服务器 - self.server = DashboardServer( - host=self._config["host"], - port=self._config["port"], - username=self._config["username"], - password=self._config["password"] - ) - - # 如果配置为自动启动,则启动服务器 - if self._config["auto_start"]: - self.start_server() - - return True - - def start_server(self) -> bool: - """启动看板服务器""" - if self.server_thread and self.server_thread.is_alive(): - self.logger.warning("服务器已经在运行中") - return False - - try: - self.server_thread = threading.Thread(target=self.server.run, daemon=True) - self.server_thread.start() - self.logger.info(f"统计看板服务器已启动,访问地址: http://{self._config['host']}:{self._config['port']}") - return True - except Exception as e: - self.logger.error(f"启动统计看板服务器失败: {e}") - return False - - def stop_server(self) -> bool: - """停止看板服务器""" - if not self.server_thread or not self.server_thread.is_alive(): - self.logger.warning("服务器未运行") - return False - - try: - # 修改:添加超时处理和错误检查 - self.server.stop() - # 等待线程结束,但设置超时 - self.server_thread.join(timeout=5) - - # 检查线程是否真的结束了 - if self.server_thread.is_alive(): - self.logger.warning("服务器停止超时,可能需要手动终止") - return False - - self.logger.info("统计看板服务器已停止") - return True - except Exception as e: - self.logger.error(f"停止统计看板服务器失败: {e}") - return False - - def match_command(self, content: str) -> bool: - """匹配命令""" - return content.strip().startswith("/stats") - - def process_message(self, message: Dict[str, Any]) -> Tuple[bool, str]: - return False, "" - - def shutdown(self) -> None: - """关闭插件""" - if self.server: - self.stop_server() - - def start(self) -> bool: - """启动插件""" - self.LOG.info(f"[{self.name}] 插件已启动") - self.status = PluginStatus.RUNNING - return True - - def stop(self) -> bool: - """停止插件""" - self.LOG.info(f"[{self.name}] 插件已停止") - self.status = PluginStatus.STOPPED - return True diff --git a/plugins/stats_dashboard/static/favicon.ico b/plugins/stats_dashboard/static/favicon.ico deleted file mode 100644 index d0f92da3c30d2fd1c53f6927ebdd4ce9e745b7fa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15406 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 diff --git a/plugins/stats_dashboard/templates/base.html b/plugins/stats_dashboard/templates/base.html deleted file mode 100644 index 7de94ed..0000000 --- a/plugins/stats_dashboard/templates/base.html +++ /dev/null @@ -1,186 +0,0 @@ - - - - - - {% block title %}机器人管理后台{% endblock %} - - - - - - - - - - - - - - - {% block head %}{% endblock %} - - -
-
-

机器人管理后台

-
- -
- - - - -
- - - - - - - - - - - - - - 刷新数据 - - - - - - - - {% block content %}{% endblock %} -
-
-
- - - - - {% block scripts %}{% endblock %} - - \ No newline at end of file diff --git a/plugins/stats_dashboard/templates/errors.html b/plugins/stats_dashboard/templates/errors.html deleted file mode 100644 index 60b6b78..0000000 --- a/plugins/stats_dashboard/templates/errors.html +++ /dev/null @@ -1,146 +0,0 @@ -{% extends "base.html" %} - -{% block title %}错误日志 - 机器人管理后台{% endblock %} - -{% block content %} - -
- - - -
- 错误日志 -
- - - - - - - - - - - - - - - -
- - -
-
-
-
- - - -
-

插件名称: {% raw %}{{ errorDetail.plugin_name }}{% endraw %}

-

命令: {% raw %}{{ errorDetail.command }}{% endraw %}

-

用户ID: {% raw %}{{ errorDetail.user_id }}{% endraw %}

-

群组ID: {% raw %}{{ errorDetail.group_id || '无' }}{% endraw %}

-

时间: {% raw %}{{ errorDetail.created_at }}{% endraw %}

-

错误信息: {% raw %}{{ errorDetail.error_message }}{% endraw %}

-
-

堆栈跟踪:

-
{% raw %}{{ errorDetail.stack_trace }}{% endraw %}
-
-
-
-
-{% endblock %} - -{% block scripts %} - -{% endblock %} \ No newline at end of file diff --git a/plugins/stats_dashboard/templates/groups.html b/plugins/stats_dashboard/templates/groups.html deleted file mode 100644 index 1932d11..0000000 --- a/plugins/stats_dashboard/templates/groups.html +++ /dev/null @@ -1,79 +0,0 @@ -{% extends "base.html" %} - -{% block title %}群组统计 - 机器人管理后台{% endblock %} - -{% block content %} - -
- - - -
- 群组活跃度排行 -
- - - - - - - - - - - - - - - - -
-
-
-
-{% endblock %} - -{% block scripts %} - -{% endblock %} \ No newline at end of file diff --git a/plugins/stats_dashboard/templates/index.html b/plugins/stats_dashboard/templates/index.html deleted file mode 100644 index 584c62e..0000000 --- a/plugins/stats_dashboard/templates/index.html +++ /dev/null @@ -1,386 +0,0 @@ -{% extends "base.html" %} - -{% block title %}首页概览 - 机器人管理后台{% endblock %} - -{% block content %} - -
- - - -
- 总调用次数 -
-
- {% raw %}{{ totalCalls }}{% endraw %} -
-
-
- - -
- 成功率 -
-
- {% raw %}{{ successRate.toFixed(2) }}{% endraw %}% -
-
-
- - -
- 活跃用户数 -
-
- {% raw %}{{ activeUsers }}{% endraw %} -
-
-
- - -
- 活跃群组数 -
-
- {% raw %}{{ activeGroups }}{% endraw %} -
-
-
- - -
- 平均响应时间 -
-
- {% raw %}{{ avgResponseTime.toFixed(2) }}{% endraw %} ms -
-
-
-
- - - - - -
- 热门用户 -
- - - - - - - - -
-
- - -
- 热门群组 -
- - - - - - - -
-
- - -
- 热门插件 -
- - - - -
-
-
- - - -
-

插件使用排行

- -
-
- -
-

成功率分析

- -
-
-
- - - -
-

使用趋势

- -
-
-
-
-{% endblock %} - -{% block scripts %} - -{% endblock %} - -{% block styles %} - -{% endblock %} \ No newline at end of file diff --git a/plugins/stats_dashboard/templates/plugins.html b/plugins/stats_dashboard/templates/plugins.html deleted file mode 100644 index 06a8845..0000000 --- a/plugins/stats_dashboard/templates/plugins.html +++ /dev/null @@ -1,255 +0,0 @@ -{% extends "base.html" %} - -{% block title %}插件统计 - 机器人管理后台{% endblock %} - -{% block content %} - -
- - - -
- 插件使用统计 -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - -
-

{% raw %}{{ selectedPlugin ? selectedPlugin.plugin_name : '' }}{% endraw %} 使用趋势

- -
-
-
-{% endblock %} - -{% block scripts %} - -{% endblock %} \ No newline at end of file diff --git a/plugins/stats_dashboard/templates/robot_management.html b/plugins/stats_dashboard/templates/robot_management.html deleted file mode 100644 index 984a6c4..0000000 --- a/plugins/stats_dashboard/templates/robot_management.html +++ /dev/null @@ -1,473 +0,0 @@ -{% extends "base.html" %} - -{% block title %}群机器人管理 - 机器人管理后台{% endblock %} - -{% block content %} - -
- - - -
- 群机器人管理 - - 添加群组 - - - -
- - - - - - - - - - - - - - - - -
- - 已选择 {% raw %}{{ selectedGroups.length }}{% endraw %} 个群组 - -
- 批量启用 - 批量关闭 - 批量移除 -
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - 取消 - 确定 - - - - - -
-

{% raw %}{{ currentGroupName }}{% endraw %} 消息趋势

- -
-
-
-{% endblock %} - -{% block scripts %} - -{% endblock %} \ No newline at end of file diff --git a/plugins/stats_dashboard/templates/users.html b/plugins/stats_dashboard/templates/users.html deleted file mode 100644 index 9561555..0000000 --- a/plugins/stats_dashboard/templates/users.html +++ /dev/null @@ -1,77 +0,0 @@ -{% extends "base.html" %} - -{% block title %}用户统计 - 机器人管理后台{% endblock %} - -{% block content %} - -
- - - -
- 用户活跃度排行 -
- - - - - - - - - - - - - - -
-
-
-
-{% endblock %} - -{% block scripts %} - -{% endblock %} \ No newline at end of file