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 d0f92da..0000000 Binary files a/plugins/stats_dashboard/static/favicon.ico and /dev/null differ 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