From dba9c315040c3ebecb9ff98a461ff21263abd25f Mon Sep 17 00:00:00 2001 From: liuwei Date: Wed, 9 Apr 2025 11:54:03 +0800 Subject: [PATCH] =?UTF-8?q?=E9=9C=80=E6=B1=82=EF=BC=9A1.=E5=8A=A0=E5=85=A5?= =?UTF-8?q?=E4=BA=86=E7=94=A8=E6=88=B7=E7=A7=AF=E5=88=86=E8=A1=A8=EF=BC=9B?= =?UTF-8?q?2.=E5=8A=A0=E5=85=A5=E4=BA=86=E6=8C=87=E4=BB=A4=E7=A7=AF?= =?UTF-8?q?=E5=88=86=E6=89=A3=E9=99=A4=E5=8A=9F=E8=83=BD=EF=BC=9B3.?= =?UTF-8?q?=E5=8A=A0=E5=85=A5=E4=BA=86=E7=A7=AF=E5=88=86=E8=8E=B7=E5=BE=97?= =?UTF-8?q?=E4=B8=8E=E6=89=A3=E9=99=A4=E6=B3=A8=E8=A7=A3=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- admin/dashboard/blueprints/robot.py | 2 +- db/points_db.py | 578 ++++++++++++++++++ {game_task => db/scripts}/game_task.sql | 0 {message_storage => db/scripts}/message.sql | 0 game_task/game_task_encyclopedia.py | 427 ------------- plugins/beautyleg/main.py | 6 +- plugins/dify/main.py | 6 +- plugins/douyin_parser/main.py | 6 +- plugins/game_task/__init__.py | 5 + plugins/game_task/config.toml | 8 + plugins/game_task/main.py | 552 +++++++++++++++++ plugins/group_add/main.py | 4 +- plugins/group_auto_invite/main.py | 6 +- plugins/group_member_change/main.py | 4 +- plugins/message_recall/main.py | 4 +- plugins/message_sign/main.py | 84 ++- plugins/message_summary/main.py | 8 +- plugins/music/main.py | 6 +- plugins/plugin_manager/main.py | 2 +- plugins/point_trade/main.py | 6 +- plugins/system_updater/main.py | 2 +- plugins/video/main.py | 6 +- plugins/video_man/main.py | 6 +- plugins/xiuren_image/main.py | 6 +- robot.py | 59 +- {game_task => utils/ai}/game_chatgpt_qa.py | 0 .../decorator/plugin_decorators.py | 0 utils/decorator/points_decorator.py | 164 +++++ .../robot_cmd}/robot_command.py | 0 .../wechat}/message_to_db.py | 3 - xiuren/main.py | 2 +- 31 files changed, 1435 insertions(+), 527 deletions(-) create mode 100644 db/points_db.py rename {game_task => db/scripts}/game_task.sql (100%) rename {message_storage => db/scripts}/message.sql (100%) delete mode 100644 game_task/game_task_encyclopedia.py create mode 100644 plugins/game_task/__init__.py create mode 100644 plugins/game_task/config.toml create mode 100644 plugins/game_task/main.py rename {game_task => utils/ai}/game_chatgpt_qa.py (100%) rename plugins/stats_collector/decorators.py => utils/decorator/plugin_decorators.py (100%) create mode 100644 utils/decorator/points_decorator.py rename {robot_cmd => utils/robot_cmd}/robot_command.py (100%) rename {message_storage => utils/wechat}/message_to_db.py (99%) diff --git a/admin/dashboard/blueprints/robot.py b/admin/dashboard/blueprints/robot.py index b5bf081..c7cbe42 100644 --- a/admin/dashboard/blueprints/robot.py +++ b/admin/dashboard/blueprints/robot.py @@ -1,7 +1,7 @@ from flask import Blueprint, render_template, jsonify, request, current_app from .auth import login_required import logging -from robot_cmd.robot_command import GroupBotManager, Feature, PermissionStatus +from utils.robot_cmd.robot_command import GroupBotManager, Feature, PermissionStatus from datetime import datetime # 创建机器人管理蓝图 diff --git a/db/points_db.py b/db/points_db.py new file mode 100644 index 0000000..c99cbe9 --- /dev/null +++ b/db/points_db.py @@ -0,0 +1,578 @@ +# -*- coding: utf-8 -*- +""" +积分系统数据库操作类 +""" +import logging +from datetime import datetime +from enum import Enum +from typing import Dict, List, Optional, Tuple, Any + +from db.base import BaseDBOperator +from db.connection import DBConnectionManager + + +class PointSource(Enum): + """积分来源枚举""" + CHECKIN = "checkin" # 签到 + GAME = "game" # 游戏 + ADMIN = "admin" # 管理员操作 + TRADE = "trade" # 积分交易 + PLUGIN = "plugin" # 插件使用 + OTHER = "other" # 其他 + + +class PointsDBOperator(BaseDBOperator): + """积分系统数据库操作类""" + + def __init__(self, db_manager=None): + """初始化积分数据库操作类""" + super().__init__(db_manager or DBConnectionManager.get_instance()) + self.logger = logging.getLogger("PointsDBOperator") + + # 确保数据库表存在 + self._ensure_tables_exist() + + def _ensure_tables_exist(self): + """确保积分相关的数据库表存在""" + try: + # 创建用户积分表 + self.execute_update(""" + CREATE TABLE IF NOT EXISTS t_user_points ( + id INTEGER PRIMARY KEY AUTO_INCREMENT, + user_id VARCHAR(100) NOT NULL, + group_id VARCHAR(100) NOT NULL, + total_points INTEGER DEFAULT 0, + checkin_points INTEGER DEFAULT 0, + game_points INTEGER DEFAULT 0, + other_points INTEGER DEFAULT 0, + last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + UNIQUE(user_id, group_id) + ) ENGINE=InnoDB CHARACTER SET utf8mb4; + """) + + # 创建积分交易记录表 + self.execute_update(""" + CREATE TABLE IF NOT EXISTS t_point_transactions ( + id INTEGER PRIMARY KEY AUTO_INCREMENT, + user_id VARCHAR(100) NOT NULL, + group_id VARCHAR(100) NOT NULL, + transaction_type VARCHAR(20) NOT NULL, + points INTEGER NOT NULL, + source VARCHAR(50) NOT NULL, + description TEXT, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) ENGINE=InnoDB CHARACTER SET utf8mb4; + """) + + # 创建功能插件积分配置表 + self.execute_update(""" + CREATE TABLE IF NOT EXISTS t_plugin_point_config ( + id INTEGER PRIMARY KEY AUTO_INCREMENT, + plugin_name VARCHAR(100) NOT NULL, + points_required INTEGER DEFAULT 0, + is_enabled BOOLEAN DEFAULT TRUE, + description TEXT, + UNIQUE(plugin_name) + ) ENGINE=InnoDB CHARACTER SET utf8mb4; + """) + + self.logger.info("积分系统数据库表检查/创建完成") + except Exception as e: + self.logger.error(f"创建积分系统数据库表失败: {e}") + raise + + def get_user_points(self, user_id: str, group_id: str) -> Dict: + """ + 获取用户积分信息 + + Args: + user_id: 用户ID + group_id: 群组ID + + Returns: + 包含用户积分信息的字典 + """ + try: + # 先尝试从新表获取 + result = self.execute_query(""" + SELECT * FROM t_user_points + WHERE user_id = %s AND group_id = %s + """, (user_id, group_id), fetch_one=True) + + if result: + return result + + # 如果新表没有数据,尝试从旧表迁移数据 + return self._migrate_user_points(user_id, group_id) + except Exception as e: + self.logger.error(f"获取用户积分失败: {e}") + return { + "user_id": user_id, + "group_id": group_id, + "total_points": 0, + "checkin_points": 0, + "game_points": 0, + "other_points": 0 + } + + def _migrate_user_points(self, user_id: str, group_id: str) -> Dict: + """ + 从旧表迁移用户积分数据到新表 + + Args: + user_id: 用户ID + group_id: 群组ID + + Returns: + 包含用户积分信息的字典 + """ + result = { + "user_id": user_id, + "group_id": group_id, + "total_points": 0, + "checkin_points": 0, + "game_points": 0, + "other_points": 0 + } + + try: + # 查询签到积分 + sign_result = self.execute_query(""" + SELECT points FROM t_sign_record + WHERE wx_id = %s AND group_id = %s + """, (user_id, group_id), fetch_one=True) + + # 查询游戏积分 + game_result = self.execute_query(""" + SELECT points FROM t_encyclopedia_players + WHERE player_id = %s AND group_id = %s + """, (user_id, group_id), fetch_one=True) + + # 合并积分 + checkin_points = sign_result["points"] if sign_result else 0 + game_points = game_result["points"] if game_result else 0 + total_points = checkin_points + game_points + + # 更新结果 + result["checkin_points"] = checkin_points + result["game_points"] = game_points + result["total_points"] = total_points + + # 插入到新表 + self.execute_update(""" + INSERT INTO t_user_points + (user_id, group_id, total_points, checkin_points, game_points, other_points) + VALUES (%s, %s, %s, %s, %s, %s) + """, (user_id, group_id, total_points, checkin_points, game_points, 0)) + + # 获取插入后的完整记录 + return self.execute_query(""" + SELECT * FROM t_user_points + WHERE user_id = %s AND group_id = %s + """, (user_id, group_id), fetch_one=True) or result + except Exception as e: + self.logger.error(f"迁移用户积分失败: {e}") + return result + + def add_points(self, user_id: str, group_id: str, points: int, + source: PointSource, description: str = None, + user_name: str = None) -> Tuple[bool, Dict]: + """ + 增加用户积分 + + Args: + user_id: 用户ID + group_id: 群组ID + points: 积分数量 + source: 积分来源 + description: 描述 + user_name: 用户名称 + + Returns: + (成功标志, 用户积分信息) + """ + if points <= 0: + return False, {"error": "积分必须为正数"} + + try: + # 检查用户是否存在 + user_exists = self.execute_query(""" + SELECT * FROM t_user_points + WHERE user_id = %s AND group_id = %s + """, (user_id, group_id), fetch_one=True) + + if not user_exists: + # 如果用户不存在,先迁移或创建用户 + self._migrate_user_points(user_id, group_id) + + # 更新积分 + source_field = f"{source.value}_points" if source.value in ["checkin", "game"] else "other_points" + + self.execute_update(f""" + UPDATE t_user_points + SET total_points = total_points + %s, + {source_field} = {source_field} + %s + WHERE user_id = %s AND group_id = %s + """, (points, points, user_id, group_id)) + + # 记录交易 + self.execute_update(""" + INSERT INTO t_point_transactions + (user_id, group_id, transaction_type, points, source, description) + VALUES (%s, %s, %s, %s, %s, %s) + """, (user_id, group_id, "earn", points, source.value, description)) + + # 同时更新旧表,保持兼容 + if source == PointSource.CHECKIN: + self.execute_update(""" + UPDATE t_sign_record + SET points = points + %s + WHERE wx_id = %s AND group_id = %s + """, (points, user_id, group_id)) + elif source == PointSource.GAME: + self.execute_update(""" + UPDATE t_encyclopedia_players + SET points = points + %s + WHERE player_id = %s AND group_id = %s + """, (points, user_id, group_id)) + + # 获取更新后的积分信息 + updated_points = self.execute_query(""" + SELECT * FROM t_user_points + WHERE user_id = %s AND group_id = %s + """, (user_id, group_id), fetch_one=True) + + return True, updated_points + except Exception as e: + self.logger.error(f"增加用户积分失败: {e}") + return False, {"error": str(e)} + + def deduct_points(self, user_id: str, group_id: str, points: int, + source: PointSource, description: str = None) -> Tuple[bool, Dict]: + """ + 扣除用户积分 + + Args: + user_id: 用户ID + group_id: 群组ID + points: 积分数量 + source: 积分来源 + description: 描述 + + Returns: + (成功标志, 用户积分信息) + """ + if points <= 0: + return False, {"error": "积分必须为正数"} + + try: + # 检查用户是否存在及积分是否足够 + user_points = self.execute_query(""" + SELECT * FROM t_user_points + WHERE user_id = %s AND group_id = %s + """, (user_id, group_id), fetch_one=True) + + if not user_points: + # 如果用户不存在,先迁移或创建用户 + user_points = self._migrate_user_points(user_id, group_id) + + if user_points["total_points"] < points: + return False, {"error": "积分不足", "current_points": user_points["total_points"]} + + # 更新积分 + self.execute_update(""" + UPDATE t_user_points + SET total_points = total_points - %s + WHERE user_id = %s AND group_id = %s + """, (points, user_id, group_id)) + + # 记录交易 + self.execute_update(""" + INSERT INTO t_point_transactions + (user_id, group_id, transaction_type, points, source, description) + VALUES (%s, %s, %s, %s, %s, %s) + """, (user_id, group_id, "spend", -points, source.value, description)) + + # 获取更新后的积分信息 + updated_points = self.execute_query(""" + SELECT * FROM t_user_points + WHERE user_id = %s AND group_id = %s + """, (user_id, group_id), fetch_one=True) + + return True, updated_points + except Exception as e: + self.logger.error(f"扣除用户积分失败: {e}") + return False, {"error": str(e)} + + def transfer_points(self, from_user_id: str, to_user_id: str, group_id: str, + points: int, description: str = None) -> Tuple[bool, Dict]: + """ + 转移积分从一个用户到另一个用户 + + Args: + from_user_id: 转出用户ID + to_user_id: 转入用户ID + group_id: 群组ID + points: 积分数量 + description: 描述 + + Returns: + (成功标志, 结果信息) + """ + if points <= 0: + return False, {"error": "积分必须为正数"} + + try: + # 先扣除转出用户积分 + success, result = self.deduct_points( + from_user_id, group_id, points, + PointSource.TRADE, f"转账给用户 {to_user_id}: {description}" + ) + + if not success: + return False, result + + # 再增加转入用户积分 + success, to_result = self.add_points( + to_user_id, group_id, points, + PointSource.TRADE, f"收到用户 {from_user_id} 的转账: {description}" + ) + + if not success: + # 如果增加失败,回滚扣除操作 + self.add_points( + from_user_id, group_id, points, + PointSource.TRADE, f"转账失败退回: {description}" + ) + return False, to_result + + return True, { + "from_user": result, + "to_user": to_result + } + except Exception as e: + self.logger.error(f"转移用户积分失败: {e}") + return False, {"error": str(e)} + + def get_user_transactions(self, user_id: str, group_id: str, limit: int = 10) -> List[Dict]: + """ + 获取用户积分交易记录 + + Args: + user_id: 用户ID + group_id: 群组ID + limit: 记录数量限制 + + Returns: + 交易记录列表 + """ + try: + return self.execute_query(""" + SELECT * FROM t_point_transactions + WHERE user_id = %s AND group_id = %s + ORDER BY created_at DESC + LIMIT %s + """, (user_id, group_id, limit)) + except Exception as e: + self.logger.error(f"获取用户交易记录失败: {e}") + return [] + + def get_points_ranking(self, group_id: str, limit: int = 10) -> List[Dict]: + """ + 获取群组积分排行榜 + + Args: + group_id: 群组ID + limit: 记录数量限制 + + Returns: + 排行榜列表 + """ + try: + return self.execute_query(""" + SELECT user_id, total_points, checkin_points, game_points, other_points + FROM t_user_points + WHERE group_id = %s + ORDER BY total_points DESC + LIMIT %s + """, (group_id, limit)) + except Exception as e: + self.logger.error(f"获取积分排行榜失败: {e}") + return [] + + def get_plugin_config(self, plugin_name: str) -> Optional[Dict]: + """ + 获取插件积分配置 + + Args: + plugin_name: 插件名称 + + Returns: + 插件积分配置 + """ + try: + return self.execute_query(""" + SELECT * FROM t_plugin_point_config + WHERE plugin_name = %s + """, (plugin_name,), fetch_one=True) + except Exception as e: + self.logger.error(f"获取插件积分配置失败: {e}") + return None + + def set_plugin_config(self, plugin_name: str, points_required: int, + is_enabled: bool = True, description: str = None) -> bool: + """ + 设置插件积分配置 + + Args: + plugin_name: 插件名称 + points_required: 所需积分 + is_enabled: 是否启用 + description: 描述 + + Returns: + 是否成功 + """ + try: + self.execute_update(""" + INSERT INTO t_plugin_point_config + (plugin_name, points_required, is_enabled, description) + VALUES (%s, %s, %s, %s) + ON DUPLICATE KEY UPDATE + points_required = VALUES(points_required), + is_enabled = VALUES(is_enabled), + description = VALUES(description) + """, (plugin_name, points_required, is_enabled, description)) + return True + except Exception as e: + self.logger.error(f"设置插件积分配置失败: {e}") + return False + + def get_all_plugin_configs(self) -> List[Dict]: + """ + 获取所有插件积分配置 + + Returns: + 所有插件积分配置 + """ + try: + return self.execute_query("SELECT * FROM t_plugin_point_config") + except Exception as e: + self.logger.error(f"获取所有插件积分配置失败: {e}") + return [] + + def check_plugin_points(self, user_id: str, group_id: str, plugin_name: str) -> Tuple[bool, Dict]: + """ + 检查用户是否有足够积分使用插件 + + Args: + user_id: 用户ID + group_id: 群组ID + plugin_name: 插件名称 + + Returns: + (是否有足够积分, 结果信息) + """ + try: + # 获取插件积分配置 + plugin_config = self.get_plugin_config(plugin_name) + + # 如果插件未配置或未启用积分限制,直接返回成功 + if not plugin_config or not plugin_config["is_enabled"]: + return True, {"message": "插件未配置积分限制"} + + # 获取用户积分 + user_points = self.get_user_points(user_id, group_id) + + # 检查积分是否足够 + if user_points["total_points"] < plugin_config["points_required"]: + return False, { + "error": "积分不足", + "current_points": user_points["total_points"], + "required_points": plugin_config["points_required"] + } + + return True, { + "message": "积分充足", + "current_points": user_points["total_points"], + "required_points": plugin_config["points_required"] + } + except Exception as e: + self.logger.error(f"检查插件积分失败: {e}") + return True, {"error": str(e)} # 出错时默认允许使用 + + def use_plugin(self, user_id: str, group_id: str, plugin_name: str) -> Tuple[bool, Dict]: + """ + 使用插件并扣除积分 + + Args: + user_id: 用户ID + group_id: 群组ID + plugin_name: 插件名称 + + Returns: + (是否成功, 结果信息) + """ + try: + # 先检查积分是否足够 + can_use, result = self.check_plugin_points(user_id, group_id, plugin_name) + + if not can_use: + return False, result + + # 如果插件未配置积分或不需要扣除积分,直接返回成功 + if "required_points" not in result or result["required_points"] <= 0: + return True, {"message": "无需扣除积分"} + + # 扣除积分 + return self.deduct_points( + user_id, group_id, result["required_points"], + PointSource.PLUGIN, f"使用插件: {plugin_name}" + ) + except Exception as e: + self.logger.error(f"使用插件扣除积分失败: {e}") + return False, {"error": str(e)} + + def get_user_points_stats(self, group_id: str) -> Dict[str, Any]: + """ + 获取群组积分统计信息 + + Args: + group_id: 群组ID + + Returns: + 统计信息 + """ + stats = { + "total_users": 0, + "total_points": 0, + "avg_points": 0, + "max_points": 0, + "min_points": 0, + "checkin_points_total": 0, + "game_points_total": 0, + "other_points_total": 0 + } + + try: + result = self.execute_query(""" + SELECT + COUNT(*) as total_users, + SUM(total_points) as total_points, + AVG(total_points) as avg_points, + MAX(total_points) as max_points, + MIN(total_points) as min_points, + SUM(checkin_points) as checkin_points_total, + SUM(game_points) as game_points_total, + SUM(other_points) as other_points_total + FROM t_user_points + WHERE group_id = %s + """, (group_id,), fetch_one=True) + + if result: + stats.update({k: v or 0 for k, v in result.items()}) + + return stats + except Exception as e: + self.logger.error(f"获取群组积分统计信息失败: {e}") + return stats \ No newline at end of file diff --git a/game_task/game_task.sql b/db/scripts/game_task.sql similarity index 100% rename from game_task/game_task.sql rename to db/scripts/game_task.sql diff --git a/message_storage/message.sql b/db/scripts/message.sql similarity index 100% rename from message_storage/message.sql rename to db/scripts/message.sql diff --git a/game_task/game_task_encyclopedia.py b/game_task/game_task_encyclopedia.py deleted file mode 100644 index 943e5e4..0000000 --- a/game_task/game_task_encyclopedia.py +++ /dev/null @@ -1,427 +0,0 @@ -import random -from datetime import datetime - -from game_task.game_chatgpt_qa import game_question_json, game_answer_json -from db.connection import DBConnectionManager -from db.encyclopedia import EncyclopediaDB - -# 获取数据库连接管理器的单例 -db_manager = DBConnectionManager.get_instance() -encyclopedia_db = EncyclopediaDB(db_manager) - - -# 添加群聊 -def add_group(group_id, player_id): - try: - result = encyclopedia_db.add_group(group_id) - if result: - message = f"🎉 群 {group_id} 已就位,准备开燥!" - else: - message = f"🌟 群 {group_id} 早就蓄势待发啦!" - return {"message": message, "player_id": player_id} - except Exception as e: - print(f"添加群聊出错: {e}") - message = f"🌟 群 {group_id} 早就蓄势待发啦!" - return {"message": message, "player_id": player_id} - - -# 获取所有群聊ID -def get_group_ids(): - try: - return encyclopedia_db.get_all_groups() - except Exception as e: - print(f"获取群聊ID出错: {e}") - return [] - - -# 确保游戏启动(自动初始化群聊和玩家) -def ensure_game_started(group_id, player_id, player_name="未知玩家"): - try: - # 检查并添加群聊 - if not encyclopedia_db.check_group_exists(group_id): - add_group(group_id, player_id) - - # 检查并添加玩家 - player = encyclopedia_db.get_player(player_id, group_id) - if not player: - encyclopedia_db.add_player(player_id, group_id, player_name) - message = ( - f"🎉 哇塞,{player_name} 你来啦!\n" - f"🌟 群 {group_id} 瞬间燃爆!\n" - f"🎈 快来接任务,秀翻全场!指令: /t" - ) - return {"message": message, "player_id": player_id} - return {"message": None, "player_id": player_id} - except Exception as e: - print(f"确保游戏启动出错: {e}") - return {"message": None, "player_id": player_id} - - -# 随机分配任务 -def assign_random_task(group_id, player_id=None): - try: - # 获取群内所有玩家 - players = encyclopedia_db.get_all_players_in_group(group_id) - - if not players: - message = ( - f"😔 哎呀,群 {group_id} 静悄悄\n" - f"🌟 快拉小伙伴来嗨吧!" - ) - return {"message": message, "player_id": player_id} - - if player_id: - player_dict = {p['player_id']: p['player_name'] for p in players} - if player_id not in player_dict: - message = ( - f"😅 嘿,{player_id} 你谁啊?\n" - f"🌟 先用 /s 报名,不然没法玩哦!" - ) - return {"message": message, "player_id": player_id} - holder_id = player_id - holder_name = player_dict[player_id] - else: - holder = random.choice(players) - holder_id = holder['player_id'] - holder_name = holder['player_name'] - - task = game_question_json("请出题!") - category = task["category"] - question = task["question"] - answer = task["answer"] - score = int(task["score"]) - description = task.get("description", "") - - # 创建活跃任务 - active_task_id = encyclopedia_db.create_active_task( - group_id, question, answer, score, description, holder_id - ) - - if not active_task_id: - message = f"😔 任务创建失败,请稍后再试!" - return {"message": message, "player_id": player_id} - - if player_id: - message = ( - f"🎁 {holder_name},你的专属任务闪亮登场!\n" - f"🎀 任务ID: {active_task_id}\n" - f"🎈 问题:[{category}]{question}\n" - f"🌼 积分:{score}\n" - f"🌈 快上答案:/a {active_task_id} 答案" - ) - else: - message = ( - f"🎁 新任务来袭,够不够刺激?\n" - f"🎀 任务ID: {active_task_id}\n" - f"🌟 幸运鹅:{holder_name}\n" - f"🎈 问题:[{category}]{question}\n" - f"🌼 积分:{score}\n" - f"🌈 抢答格式:/a {active_task_id} 答案" - ) - return {"message": message, "player_id": holder_id} - except Exception as e: - print(f"分配任务出错: {e}") - message = f"😔 任务分配出错,请稍后再试!" - return {"message": message, "player_id": player_id} - - -# 提交答案并计分 -def submit_answer(group_id, player_id, task_id, answer): - try: - # 获取玩家信息 - player = encyclopedia_db.get_player(player_id, group_id) - if not player: - message = ( - f"😅 嘿,{player_id} 你是路人甲吗?\n" - f"🌟 用 /s 先加入群 {group_id} 吧!" - ) - return {"message": message, "player_id": player_id} - - player_name = player['player_name'] - - if not task_id.isdigit(): - message = ( - f"😅 喂,任务ID得是数字好吗?\n" - f"🌟 比如:1\n" - f"🎈 别瞎搞,重新来!" - ) - return {"message": message, "player_id": player_id} - - active_task_id = int(task_id) - - # 获取任务信息 - task_data = encyclopedia_db.get_task_by_id(group_id, active_task_id) - - if not task_data: - message = ( - f"😔 哎哟,任务 task_{active_task_id} 不翼而飞啦!\n" - f"🌼 可能被别人抢先一步咯!" - ) - return {"message": message, "player_id": player_id} - - if task_data['status'] == 'completed': - message = ( - f"😄 哈哈,你慢了一步!\n" - f"🌟 任务 task_{active_task_id} 已经完结\n" - f"🎈 快去抢新任务吧!" - ) - return {"message": message, "player_id": player_id} - - question = task_data['question'] - correct_answer_db = task_data['answer'].lower() - top_score = task_data['score'] - holder_id = task_data['holder_id'] - - # 获取任务持有者信息 - holder = encyclopedia_db.get_task_holder(group_id, holder_id) - holder_name = holder['player_name'] if holder else "未知玩家" - - answer_json = {"question": question, "top_score": str(top_score), "answer": answer} - result = game_answer_json(answer_json) - points = int(result["score"]) - description = result["description"] - is_correct = points > 0 - - if is_correct: - # 更新玩家积分 - encyclopedia_db.update_player_points(player_id, group_id, points) - - # 完成任务 - encyclopedia_db.complete_task(active_task_id) - - if player_id == holder_id: - message = ( - f"🎉 {player_name} 你是天才吗?\n" - f"🌟 任务:{question}\n" - f"🎈 答对啦,简直无敌!\n" - f"🌈 奖励:{points} 分\n" - f"🎀 彩蛋:{description}" - ) - else: - message = ( - f"🎉 {player_name} 抢答王上线!\n" - f"🌟 任务:{question}\n" - f"🎈 原主:{holder_name} 被你截胡啦!\n" - f"🌈 狂揽 {points} 分,太骚了!\n" - f"🎀 彩蛋:{description}" - ) - else: - # 扣除积分 - encyclopedia_db.update_player_points(player_id, group_id, -1) - points = -1 - message = ( - f"😅 {player_name} 你这是要笑死我吗?\n" - f"🌼 任务:{question}\n" - f"🎈 你答:{answer}\n" - f"🌟 正确答案:{correct_answer_db}\n" - f"🌈 扣 1 分,别哭哦!\n" - f"🎀 提示:{description}\n" - f"🌟 任务ID: {active_task_id} 还能抢救一下!" - ) - - # 添加任务历史记录 - encyclopedia_db.add_task_history( - group_id, active_task_id, player_id, answer, is_correct, points - ) - - return {"message": message, "player_id": player_id} - except ValueError as e: - print(f"submit_answer:{e}") - message = ( - f"😅 喂,任务ID得是数字懂吗?\n" - f"🌟 比如:1\n" - f"🎈 重来,别皮!" - ) - return {"message": message, "player_id": player_id} - except Exception as e: - print(f"submit_answer:{e}") - message = ( - f"😅 出错了,任务ID得是数字哦!\n" - f"🌟 比如:1\n" - f"🎈 再试一次吧!" - ) - return {"message": message, "player_id": player_id} - - -# 显示排行榜 -def show_rank(group_id, player_id): - try: - # 获取排行榜 - ranks = encyclopedia_db.get_player_ranking(group_id, 10) - - if not ranks: - message = ( - f"😔 群 {group_id} 冷冷清清\n" - f"🌟 快来一起燥起来吧!" - ) - return {"message": message, "player_id": player_id} - - rank_text = f"🎉 群 {group_id} 排行榜(Top 10)来啦!\n" - for i, row in enumerate(ranks, 1): - rank_text += f"🐓 {i}. {row['player_name']}: {row['points']} 分\n" - return {"message": rank_text, "player_id": player_id} - except Exception as e: - print(f"显示排行榜出错: {e}") - message = f"😔 获取排行榜出错,请稍后再试!" - return {"message": message, "player_id": player_id} - - -# 显示当前活跃任务 -def show_active_tasks(group_id, player_id): - try: - # 获取活跃任务 - tasks = encyclopedia_db.get_active_tasks_in_group(group_id) - - if not tasks: - message = ( - f"😄 群 {group_id} 现在一片祥和\n" - f"🌟 没任务?快用 /t 搞一个!" - ) - return {"message": message, "player_id": player_id} - - task_text = f"🎉 群 {group_id} 活跃任务速递:\n" - for task in tasks: - task_text += ( - f"🌈 任务ID: task_{task['active_task_id']}\n" - f"🎀 问题:{task['question']}\n" - f"🌼 大佬:{task['player_name']}\n" - ) - return {"message": task_text, "player_id": player_id} - except Exception as e: - print(f"显示活跃任务出错: {e}") - message = f"😔 获取活跃任务出错,请稍后再试!" - return {"message": message, "player_id": player_id} - - -# 列举所有未完成任务 -def list_uncompleted_tasks(group_id, player_id): - try: - # 获取未完成任务 - tasks = encyclopedia_db.get_active_tasks_in_group(group_id) - - if not tasks: - message = ( - f"😄 群 {group_id} 全员开挂?\n" - f"🌟 没未完成任务,快用 /t 再战!" - ) - return {"message": message, "player_id": player_id} - - task_text = f"🎉 群 {group_id} 未完成任务大曝光:\n" - for task in tasks: - task_text += ( - f"🌈 任务ID: task_{task['active_task_id']}\n" - f"🎀 问题:{task['question']}\n" - f"🌼 主人:{task['player_name']}\n" - ) - return {"message": task_text, "player_id": player_id} - except Exception as e: - print(f"列举未完成任务出错: {e}") - message = f"😔 获取未完成任务出错,请稍后再试!" - return {"message": message, "player_id": player_id} - - -# 定时任务:整点触发,排除23:00-08:00 -def run_random_task_assignment(group_id): - current_hour = datetime.now().hour - if current_hour >= 23 or current_hour < 9: - message = ( - f"😴 现在 {current_hour}:00,小伙伴们都睡啦\n" - f"🌙 9点到22点再来嗨吧!" - ) - print(f"{datetime.now()} 群 {group_id} 当前时间 {current_hour}:00 在23:00-08:00区间,跳过任务发放") - return {"message": message, "player_id": None} - result = assign_random_task(group_id) - print(f"{datetime.now()} {result['message']}") - return result - - -# 处理群聊消息 -def game_process_message(group_id, player_id, message, player_name="未知玩家"): - init_result = ensure_game_started(group_id, player_id, player_name) - init_message = init_result["message"] - player_id = init_result["player_id"] - - if message == "/s": # 可选:显式加入游戏 - if init_message: - return {"message": init_message, "player_id": player_id} - message = ( - f"😄 嘿,{player_name} 你来啦!\n" - f"🌼 已加入群 {group_id} 的狂欢\n" - f"🎶 快用 /t 开干吧!" - ) - return {"message": message, "player_id": player_id} - elif message == "/ts": # 查看活跃任务 - if init_message: - message = init_message + "\n🌟 用 /ts 看看当前任务吧!" - return {"message": message, "player_id": player_id} - return show_active_tasks(group_id, player_id) - elif message == "/l": # 列出未完成任务 - if init_message: - message = init_message + "\n🌟 用 /l 查未完成任务哦!" - return {"message": message, "player_id": player_id} - return list_uncompleted_tasks(group_id, player_id) - elif message.startswith("/a"): # 提交答案 - if init_message: - message = init_message + "\n🌟 用 /a [任务ID] [答案] 放大招吧!" - return {"message": message, "player_id": player_id} - parts = message.split(" ", 2) - if len(parts) < 3: - message = ( - f"😅 喂,格式不对啊!\n" - f"🌟 正确姿势:/a [任务ID] [答案]\n" - f"🎈 比如:/a 1 钒" - ) - return {"message": message, "player_id": player_id} - task_id, answer = parts[1], parts[2] - return submit_answer(group_id, player_id, task_id, answer) - elif message == "/r": # 查看排行榜 - if init_message: - message = init_message + "\n🌟 用 /r 看看谁是大佬!" - return {"message": message, "player_id": player_id} - return show_rank(group_id, player_id) - elif message == "/ag": # 可选:手动添加群聊 - if init_message: - return {"message": init_message, "player_id": player_id} - return add_group(group_id, player_id) - elif message == "/t": # 获取任务 - if init_message: - message = init_message + "\n🌟 已为你准备好任务,快接招!" - return {"message": message, "player_id": player_id} - current_hour = datetime.now().hour - if current_hour >= 23 or current_hour < 9: - message = ( - f"😴 现在 {current_hour}:00\n" - f"🌙 太晚啦,大家都睡了!\n" - f"🌞 9点到22点再来嗨吧!" - ) - return {"message": message, "player_id": player_id} - return assign_random_task(group_id, player_id) - else: - message = ( - f"😄 嘿,二货!\n" - f"🌟 指令玩错啦!\n" - f"🎈 快试试这些:\n" - f"🌈 /s - 加入狂欢(可选)\n" - f"🎀 /ts - 看活跃任务\n" - f"🌼 /l - 未完成任务\n" - f"🌟 /a [任务ID] [答案] - 提交答案\n" - f"🎈 /ag - 加群(可选)\n" - f"🌈 /t - 抢任务\n" - f"🎀 /r - 谁是大佬" - ) - return {"message": message, "player_id": player_id} - - -# 设置定时任务 -def setup_schedule(): - group_ids = get_group_ids() - for gid in group_ids: - run_random_task_assignment(group_id=gid) - - -# 主程序 -if __name__ == "__main__": - # 测试用例 - print(game_process_message("45317011307@chatroom", "Jyunere", "/t")) # 新用户获取任务 - print(game_process_message("45317011307@chatroom", "Jyunere", "/a 18 罗马斗兽场")) # 提交答案 - print(game_process_message("45317011307@chatroom", "Jyunere", "/r")) # 查看排行榜 diff --git a/plugins/beautyleg/main.py b/plugins/beautyleg/main.py index 67ee298..0e82a84 100644 --- a/plugins/beautyleg/main.py +++ b/plugins/beautyleg/main.py @@ -8,8 +8,9 @@ from wcferry import Wcf from message_util import MessageUtil from plugin_common.message_plugin_interface import MessagePluginInterface from plugin_common.plugin_interface import PluginStatus -from plugins.stats_collector.decorators import plugin_stats_decorator -from robot_cmd.robot_command import Feature, PermissionStatus, GroupBotManager +from utils.decorator.plugin_decorators import plugin_stats_decorator +from utils.robot_cmd.robot_command import Feature, PermissionStatus, GroupBotManager +from utils.decorator.points_decorator import plugin_points_cost class BeautyLegPlugin(MessagePluginInterface): @@ -90,6 +91,7 @@ class BeautyLegPlugin(MessagePluginInterface): return command in self._commands @plugin_stats_decorator(plugin_name="美腿图片") + @plugin_points_cost(1, "美腿图片消耗积分") def process_message(self, message: Dict[str, Any]) -> Tuple[bool, Optional[str]]: """处理消息""" content = str(message.get("content", "")).strip() diff --git a/plugins/dify/main.py b/plugins/dify/main.py index cffaac3..e72626e 100644 --- a/plugins/dify/main.py +++ b/plugins/dify/main.py @@ -10,8 +10,9 @@ from wcferry import Wcf from message_util import MessageUtil from plugin_common.message_plugin_interface import MessagePluginInterface from plugin_common.plugin_interface import PluginStatus -from plugins.stats_collector.decorators import plugin_stats_decorator -from robot_cmd.robot_command import Feature, PermissionStatus, GroupBotManager +from utils.decorator.plugin_decorators import plugin_stats_decorator +from utils.robot_cmd.robot_command import Feature, PermissionStatus, GroupBotManager +from utils.decorator.points_decorator import plugin_points_cost from utils.media_downloader import MediaDownloader @@ -103,6 +104,7 @@ class DifyPlugin(MessagePluginInterface): return command in self._commands @plugin_stats_decorator(plugin_name="Dify聊天") + @plugin_points_cost(2, "AI聊天消耗积分") def process_message(self, message: Dict[str, Any]) -> Tuple[bool, Optional[str]]: """处理消息""" content = str(message.get("content", "")).strip() diff --git a/plugins/douyin_parser/main.py b/plugins/douyin_parser/main.py index 1e63ebc..58b6f80 100644 --- a/plugins/douyin_parser/main.py +++ b/plugins/douyin_parser/main.py @@ -6,12 +6,12 @@ import traceback import requests from typing import Dict, Any, List, Optional, Tuple -from wcferry import Wcf, WxMsg +from wcferry import Wcf from plugin_common.message_plugin_interface import MessagePluginInterface from plugin_common.plugin_interface import PluginStatus -from plugins.stats_collector.decorators import plugin_stats_decorator -from robot_cmd.robot_command import Feature, PermissionStatus, GroupBotManager +from utils.decorator.plugin_decorators import plugin_stats_decorator +from utils.robot_cmd.robot_command import Feature, PermissionStatus, GroupBotManager class DouyinParserError(Exception): diff --git a/plugins/game_task/__init__.py b/plugins/game_task/__init__.py new file mode 100644 index 0000000..14bb40f --- /dev/null +++ b/plugins/game_task/__init__.py @@ -0,0 +1,5 @@ +from .main import GameTaskPlugin + +def get_plugin(): + """获取插件实例""" + return GameTaskPlugin() \ No newline at end of file diff --git a/plugins/game_task/config.toml b/plugins/game_task/config.toml new file mode 100644 index 0000000..2dcdb7d --- /dev/null +++ b/plugins/game_task/config.toml @@ -0,0 +1,8 @@ +enable = true +command = ["game", "任务", "/t", "/a", "/s"] +command-format = """ +🎮 游戏任务系统指令: +/s - 加入游戏 +/t - 获取任务 +/a <任务ID> <答案> - 提交答案 +""" \ No newline at end of file diff --git a/plugins/game_task/main.py b/plugins/game_task/main.py new file mode 100644 index 0000000..43344a5 --- /dev/null +++ b/plugins/game_task/main.py @@ -0,0 +1,552 @@ +import random +import logging +from datetime import datetime +from typing import Dict, Any, List, Optional, Tuple + +from wcferry import Wcf + +from message_util import MessageUtil +from plugin_common.message_plugin_interface import MessagePluginInterface +from plugin_common.plugin_interface import PluginStatus +from utils.decorator.plugin_decorators import plugin_stats_decorator +from utils.robot_cmd.robot_command import Feature, PermissionStatus, GroupBotManager +from utils.decorator.points_decorator import points_reward_decorator +from utils.ai.game_chatgpt_qa import game_question_json, game_answer_json +from db.connection import DBConnectionManager +from db.encyclopedia import EncyclopediaDB + + +class GameTaskPlugin(MessagePluginInterface): + """游戏任务插件""" + + @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 self._commands + + def __init__(self): + super().__init__() + + def initialize(self, context: Dict[str, Any]) -> bool: + """初始化插件""" + self.LOG = logging.getLogger(f"Plugin.{self.name}") + self.LOG.info(f"正在初始化 {self.name} 插件...") + + # 保存上下文对象 + self.wcf = context.get("wcf") + self.event_system = context.get("event_system") + self.message_util: MessageUtil = context.get("message_util") + + # 初始化配置 + self._commands = self._config.get("GameTask", {}).get("command", + ["game", "任务", "/t", "/a", "/s", "/r", "/l", "/h"]) + self.command_format = self._config.get("GameTask", {}).get("command-format", """ +🎮 百科问答指令: +/s - 加入游戏 +/t - 获取任务 +/a <任务ID> <答案> - 提交答案 +/r - 查看排行榜 +/l - 查看活跃任务 +/h - 查看未完成任务 + """) + self.enable = self._config.get("GameTask", {}).get("enable", True) + + # 初始化数据库连接 + self.db_manager = DBConnectionManager.get_instance() + self.encyclopedia_db = EncyclopediaDB(self.db_manager) + + # 注册定时任务 + if self.event_system: + self.event_system.add_interval_job( + self.run_random_task_assignment, + hours=1, + id="game_task_random_assignment" + ) + + self.LOG.info(f"[{self.name}] 插件初始化完成,指令:{self._commands}") + return True + + 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 + + def can_process(self, message: Dict[str, Any]) -> bool: + """检查是否可以处理该消息""" + if not self.enable: + return False + + content = str(message.get("content", "")).strip() + command = content.split(" ")[0] + + return command in self._commands + + def calculate_reward_points(self, message: Dict[str, Any], success: bool, response: str) -> int: + """计算奖励积分""" + if not success: + return 0 + return int(response) + + @plugin_stats_decorator(plugin_name="百科问答") + def process_message(self, message: Dict[str, Any]) -> Tuple[bool, Optional[str]]: + """处理消息""" + content = str(message.get("content", "")).strip() + command = content.split(" ")[0].lower() + sender = message.get("sender") + roomid = message.get("roomid", "") + wcf: Wcf = message.get("wcf") + gbm: GroupBotManager = message.get("gbm") + all_contacts = message.get("all_contacts", {}) + + self.LOG.info(f"插件执行: {self.name}:{content}") + + # 检查权限 + if roomid and gbm.get_group_permission(roomid, Feature.TASK_GAME) == PermissionStatus.DISABLED: + return False, "没有权限" + + try: + # 获取用户昵称 + wx_nick_name = all_contacts.get(sender, sender) + + if command == "/s": + self._handle_join_game(sender, roomid, wx_nick_name) + return True, "加入游戏成功" + elif command == "/t": + self._handle_get_task(sender, roomid) + return True, "获取任务成功" + elif command == "/a": + points = self._handle_submit_answer(content, sender, roomid) + return True, str(points) + elif command == "/r": + self._handle_show_rank(sender, roomid) + return True, "显示排行榜成功" + elif command == "/l": + self._handle_show_active_tasks(sender, roomid) + return True, "显示活跃任务成功" + elif command == "/h": + self._handle_list_uncompleted_tasks(sender, roomid) + return True, "列举未完成任务成功" + else: + self.message_util.send_text_msg(f"❌未知命令!\n{self.command_format}", + (roomid if roomid else sender), sender) + return False, "未知命令" + + except Exception as e: + self.LOG.error(f"处理消息出错: {e}") + return False, f"处理出错: {e}" + + def _handle_join_game(self, sender: str, roomid: str, wx_nick_name: str) -> None: + """处理加入游戏请求""" + try: + # 检查并添加群聊 + if not self.encyclopedia_db.check_group_exists(roomid): + self.encyclopedia_db.add_group(roomid) + self.message_util.send_text_msg( + f"🎉 群 {roomid} 已就位,准备开燥!", + (roomid if roomid else sender), + sender + ) + + # 检查并添加玩家 + player = self.encyclopedia_db.get_player(sender, roomid) + if not player: + self.encyclopedia_db.add_player(sender, roomid, wx_nick_name) + self.message_util.send_text_msg( + f"🎉 哇塞,{wx_nick_name} 你来啦!\n" + f"🌟 群 {roomid} 瞬间燃爆!\n" + f"🎈 快来接任务,秀翻全场!指令: /t", + (roomid if roomid else sender), + sender + ) + except Exception as e: + self.LOG.error(f"加入游戏出错: {e}") + self.message_util.send_text_msg( + f"😔 加入游戏出错,请稍后再试!", + (roomid if roomid else sender), + sender + ) + + def _handle_get_task(self, sender: str, roomid: str) -> None: + """处理获取任务请求""" + try: + # 获取群内所有玩家 + players = self.encyclopedia_db.get_all_players_in_group(roomid) + + if not players: + self.message_util.send_text_msg( + f"😔 哎呀,群 {roomid} 静悄悄\n" + f"🌟 快拉小伙伴来嗨吧!", + (roomid if roomid else sender), + sender + ) + return + + player_dict = {p['player_id']: p['player_name'] for p in players} + if sender not in player_dict: + self.message_util.send_text_msg( + f"😅 嘿,你谁啊?\n" + f"🌟 先用 /s 报名,不然没法玩哦!", + (roomid if roomid else sender), + sender + ) + return + + task = game_question_json("请出题!") + category = task["category"] + question = task["question"] + answer = task["answer"] + score = int(task["score"]) + description = task.get("description", "") + + # 创建活跃任务 + active_task_id = self.encyclopedia_db.create_active_task( + roomid, question, answer, score, description, sender + ) + + if not active_task_id: + self.message_util.send_text_msg( + f"😔 任务创建失败,请稍后再试!", + (roomid if roomid else sender), + sender + ) + return + + self.message_util.send_text_msg( + f"🎁 {player_dict[sender]},你的专属任务闪亮登场!\n" + f"🎀 任务ID: {active_task_id}\n" + f"🎈 问题:[{category}]{question}\n" + f"🌼 积分:{score}\n" + f"🌈 快上答案:/a {active_task_id} 答案", + (roomid if roomid else sender), + sender + ) + except Exception as e: + self.LOG.error(f"获取任务出错: {e}") + self.message_util.send_text_msg( + f"😔 获取任务出错,请稍后再试!", + (roomid if roomid else sender), + sender + ) + + + def calculate_game_points(self, message: Dict[str, Any], success: bool, response: str) -> int: + """计算游戏积分""" + if not success: + return 0 + try: + return int(response) + except (TypeError, ValueError): + return 0 + + @points_reward_decorator(calculate_game_points, "game", "百科答题奖励") + def _handle_submit_answer(self, content: str, sender: str, roomid: str) -> int: + """处理提交答案请求""" + try: + parts = content.split(" ", 2) + if len(parts) < 3: + self.message_util.send_text_msg( + f"😅 喂,格式不对啊!\n" + f"🌟 正确姿势:/a [任务ID] [答案]\n" + f"🎈 比如:/a 1 钒", + (roomid if roomid else sender), + sender + ) + return 0 + + task_id = parts[1] + answer = parts[2] + + # 获取玩家信息 + player = self.encyclopedia_db.get_player(sender, roomid) + if not player: + self.message_util.send_text_msg( + f"😅 嘿,你是路人甲吗?\n" + f"🌟 用 /s 先加入群 {roomid} 吧!", + (roomid if roomid else sender), + sender + ) + return 0 + + player_name = player['player_name'] + + if not task_id.isdigit(): + self.message_util.send_text_msg( + f"😅 喂,任务ID得是数字好吗?\n" + f"🌟 比如:1\n" + f"🎈 别瞎搞,重新来!", + (roomid if roomid else sender), + sender + ) + return 0 + + active_task_id = int(task_id) + + # 获取任务信息 + task_data = self.encyclopedia_db.get_task_by_id(roomid, active_task_id) + + if not task_data: + self.message_util.send_text_msg( + f"😔 哎哟,任务 task_{active_task_id} 不翼而飞啦!\n" + f"🌼 可能被别人抢先一步咯!", + (roomid if roomid else sender), + sender + ) + return 0 + + if task_data['status'] == 'completed': + self.message_util.send_text_msg( + f"😄 哈哈,你慢了一步!\n" + f"🌟 任务 task_{active_task_id} 已经完结\n" + f"🎈 快去抢新任务吧!", + (roomid if roomid else sender), + sender + ) + return 0 + + question = task_data['question'] + correct_answer_db = task_data['answer'].lower() + top_score = task_data['score'] + holder_id = task_data['holder_id'] + + # 获取任务持有者信息 + holder = self.encyclopedia_db.get_task_holder(roomid, holder_id) + holder_name = holder['player_name'] if holder else "未知玩家" + + answer_json = {"question": question, "top_score": str(top_score), "answer": answer} + result = game_answer_json(answer_json) + points = int(result["score"]) + description = result["description"] + is_correct = points > 0 + + # 记录答题历史 + self.encyclopedia_db.add_task_history( + roomid, active_task_id, sender, answer, is_correct, points + ) + + if is_correct: + # 完成任务 + self.encyclopedia_db.complete_task(active_task_id) + + if sender == holder_id: + self.message_util.send_text_msg( + f"🎉 {player_name} 你是天才吗?\n" + f"🌟 任务:{question}\n" + f"🎈 答对啦,简直无敌!\n" + f"🌈 奖励:{points} 分\n" + f"🎀 彩蛋:{description}", + (roomid if roomid else sender), + sender + ) + else: + self.message_util.send_text_msg( + f"🎉 {player_name} 抢答王上线!\n" + f"🌟 任务:{question}\n" + f"🎈 原主:{holder_name} 被你截胡啦!\n" + f"🌈 狂揽 {points} 分,太骚了!\n" + f"🎀 彩蛋:{description}", + (roomid if roomid else sender), + sender + ) + else: + # 扣除积分 + self.encyclopedia_db.update_player_points(sender, roomid, -1) + points = -1 + self.message_util.send_text_msg( + f"😅 {player_name} 你这是要笑死我吗?\n" + f"🌼 任务:{question}\n" + f"🎈 你答:{answer}\n" + f"🌟 正确答案:{correct_answer_db}\n" + f"🌈 扣 1 分,别哭哦!\n" + f"🎀 提示:{description}\n" + f"🌟 任务ID: {active_task_id} 还能抢救一下!", + (roomid if roomid else sender), + sender + ) + + return points + except Exception as e: + self.LOG.error(f"提交答案出错: {e}") + self.message_util.send_text_msg( + f"😔 提交答案出错,请稍后再试!", + (roomid if roomid else sender), + sender + ) + return 0 + + def _handle_show_rank(self, sender: str, roomid: str) -> None: + """处理显示排行榜请求""" + try: + # 获取排行榜 + ranks = self.encyclopedia_db.get_player_ranking(roomid, 10) + + if not ranks: + self.message_util.send_text_msg( + f"😔 群 {roomid} 冷冷清清\n" + f"🌟 快来一起燥起来吧!", + (roomid if roomid else sender), + sender + ) + return + + rank_text = f"🎉 群 {roomid} 排行榜(Top 10)来啦!\n" + for i, row in enumerate(ranks, 1): + rank_text += f"🐓 {i}. {row['player_name']}: {row['points']} 分\n" + + self.message_util.send_text_msg( + rank_text, + (roomid if roomid else sender), + sender + ) + except Exception as e: + self.LOG.error(f"显示排行榜出错: {e}") + self.message_util.send_text_msg( + f"😔 获取排行榜出错,请稍后再试!", + (roomid if roomid else sender), + sender + ) + + def _handle_show_active_tasks(self, sender: str, roomid: str) -> None: + """处理显示活跃任务请求""" + try: + # 获取活跃任务 + tasks = self.encyclopedia_db.get_active_tasks_in_group(roomid) + + if not tasks: + self.message_util.send_text_msg( + f"😄 群 {roomid} 现在一片祥和\n" + f"🌟 没任务?快用 /t 搞一个!", + (roomid if roomid else sender), + sender + ) + return + + task_text = f"🎉 群 {roomid} 活跃任务速递:\n" + for task in tasks: + task_text += ( + f"🌈 任务ID: task_{task['active_task_id']}\n" + f"🎀 问题:{task['question']}\n" + f"🌼 大佬:{task['player_name']}\n" + ) + + self.message_util.send_text_msg( + task_text, + (roomid if roomid else sender), + sender + ) + except Exception as e: + self.LOG.error(f"显示活跃任务出错: {e}") + self.message_util.send_text_msg( + f"😔 获取活跃任务出错,请稍后再试!", + (roomid if roomid else sender), + sender + ) + + def _handle_list_uncompleted_tasks(self, sender: str, roomid: str) -> None: + """处理列举未完成任务请求""" + try: + # 获取未完成任务 + tasks = self.encyclopedia_db.get_active_tasks_in_group(roomid) + + if not tasks: + self.message_util.send_text_msg( + f"😄 群 {roomid} 全员开挂?\n" + f"🌟 没未完成任务,快用 /t 再战!", + (roomid if roomid else sender), + sender + ) + return + + task_text = f"🎉 群 {roomid} 未完成任务大曝光:\n" + for task in tasks: + task_text += ( + f"🌈 任务ID: task_{task['active_task_id']}\n" + f"🎀 问题:{task['question']}\n" + f"🌼 主人:{task['player_name']}\n" + ) + + self.message_util.send_text_msg( + task_text, + (roomid if roomid else sender), + sender + ) + except Exception as e: + self.LOG.error(f"列举未完成任务出错: {e}") + self.message_util.send_text_msg( + f"😔 获取未完成任务出错,请稍后再试!", + (roomid if roomid else sender), + sender + ) + + def run_random_task_assignment(self) -> None: + """定时任务:整点触发,排除23:00-08:00""" + current_hour = datetime.now().hour + if current_hour >= 23 or current_hour < 9: + self.LOG.info(f"当前时间 {current_hour}:00 在23:00-08:00区间,跳过任务发放") + return + + try: + # 获取所有群聊 + groups = self.encyclopedia_db.get_all_groups() + for group in groups: + group_id = group['group_id'] + # 获取群内所有玩家 + players = self.encyclopedia_db.get_all_players_in_group(group_id) + if not players: + continue + + # 随机选择一个玩家 + holder = random.choice(players) + holder_id = holder['player_id'] + holder_name = holder['player_name'] + + # 创建任务 + task = game_question_json("请出题!") + category = task["category"] + question = task["question"] + answer = task["answer"] + score = int(task["score"]) + description = task.get("description", "") + + # 创建活跃任务 + active_task_id = self.encyclopedia_db.create_active_task( + group_id, question, answer, score, description, holder_id + ) + + if active_task_id: + self.message_util.send_text_msg( + f"🎁 新任务来袭,够不够刺激?\n" + f"🎀 任务ID: {active_task_id}\n" + f"🌟 幸运鹅:{holder_name}\n" + f"🎈 问题:[{category}]{question}\n" + f"🌼 积分:{score}\n" + f"🌈 抢答格式:/a {active_task_id} 答案", + group_id + ) + except Exception as e: + self.LOG.error(f"定时任务出错: {e}") \ No newline at end of file diff --git a/plugins/group_add/main.py b/plugins/group_add/main.py index 8a37b78..90235fb 100644 --- a/plugins/group_add/main.py +++ b/plugins/group_add/main.py @@ -3,11 +3,11 @@ import re from datetime import datetime from typing import Dict, Any, List, Optional, Tuple -from wcferry import Wcf, WxMsg +from wcferry import Wcf from plugin_common.message_plugin_interface import MessagePluginInterface from plugin_common.plugin_interface import PluginStatus -from robot_cmd.robot_command import Feature, PermissionStatus, GroupBotManager +from utils.robot_cmd.robot_command import Feature, PermissionStatus class GroupAddPlugin(MessagePluginInterface): diff --git a/plugins/group_auto_invite/main.py b/plugins/group_auto_invite/main.py index 949095a..bd7be62 100644 --- a/plugins/group_auto_invite/main.py +++ b/plugins/group_auto_invite/main.py @@ -3,12 +3,12 @@ import redis import re from typing import Dict, Any, List, Optional, Tuple -from wcferry import Wcf, WxMsg +from wcferry import Wcf from plugin_common.message_plugin_interface import MessagePluginInterface from plugin_common.plugin_interface import PluginStatus -from plugins.stats_collector.decorators import plugin_stats_decorator -from robot_cmd.robot_command import Feature, PermissionStatus, GroupBotManager +from utils.decorator.plugin_decorators import plugin_stats_decorator +from utils.robot_cmd.robot_command import GroupBotManager class GroupAutoInvitePlugin(MessagePluginInterface): diff --git a/plugins/group_member_change/main.py b/plugins/group_member_change/main.py index 75ef440..3c8799e 100644 --- a/plugins/group_member_change/main.py +++ b/plugins/group_member_change/main.py @@ -2,13 +2,13 @@ import logging import threading import time from datetime import datetime -from typing import Dict, Any, List, Optional, Set, Tuple +from typing import Dict, Any, List, Optional, Tuple from wcferry import Wcf from plugin_common.message_plugin_interface import MessagePluginInterface from plugin_common.plugin_interface import PluginStatus -from robot_cmd.robot_command import Feature, PermissionStatus, GroupBotManager +from utils.robot_cmd.robot_command import Feature, PermissionStatus, GroupBotManager from message_util import MessageUtil # 导入消息工具类 diff --git a/plugins/message_recall/main.py b/plugins/message_recall/main.py index 110d6a8..b042524 100644 --- a/plugins/message_recall/main.py +++ b/plugins/message_recall/main.py @@ -4,8 +4,8 @@ from typing import Dict, Any, List, Tuple, Optional from message_util import MessageUtil from plugin_common.message_plugin_interface import MessagePluginInterface from plugin_common.plugin_interface import PluginStatus -from plugins.stats_collector.decorators import plugin_stats_decorator -from robot_cmd.robot_command import Feature, PermissionStatus, GroupBotManager +from utils.decorator.plugin_decorators import plugin_stats_decorator +from utils.robot_cmd.robot_command import GroupBotManager from wcferry import Wcf diff --git a/plugins/message_sign/main.py b/plugins/message_sign/main.py index cc67767..e1ca7b0 100644 --- a/plugins/message_sign/main.py +++ b/plugins/message_sign/main.py @@ -3,19 +3,22 @@ import logging import pytz from typing import Dict, Any, List, Optional, Tuple -from wcferry import Wcf, WxMsg +from wcferry import Wcf from db.connection import DBConnectionManager from message_util import MessageUtil from plugin_common.message_plugin_interface import MessagePluginInterface from plugin_common.plugin_interface import PluginStatus -from plugins.stats_collector.decorators import plugin_stats_decorator -from robot_cmd.robot_command import Feature, PermissionStatus, GroupBotManager +from utils.decorator.plugin_decorators import plugin_stats_decorator +from utils.robot_cmd.robot_command import Feature, PermissionStatus, GroupBotManager from db.sign_in import SignInDB from db.sign_in_redis import SignInRedisDB import random import os +from utils.decorator.points_decorator import points_reward_decorator + + class MessageSignPlugin(MessagePluginInterface): """签到插件""" @@ -137,7 +140,47 @@ class MessageSignPlugin(MessagePluginInterface): return command in self._commands + # 添加积分计算函数 + def calculate_sign_in_points(self, message, success, response): + """计算签到奖励积分 + + Args: + message: 消息内容 + success: 处理结果 + response: 响应内容 + + Returns: + int: 奖励积分数量 + """ + sender = message.get("sender") + roomid = message.get("roomid", "") + + # 获取当前时间,带有时区信息 + current_time = datetime.now(tz=pytz.timezone(self.timezone)) + today_start = current_time.replace(hour=0, minute=0, second=0, microsecond=0) + yesterday = today_start - timedelta(days=1) + + # 获取用户的签到记录 + user_record = self.get_user_record(sender, roomid) + + # 计算连续签到天数 + streak = 1 + if user_record and user_record['sign_stat']: + last_sign_date = user_record['sign_stat'].replace(hour=0, minute=0, second=0, microsecond=0) + # 确保 sign_stat 和 today_start 是同一时区对象 + if isinstance(last_sign_date, datetime) and last_sign_date.tzinfo is None: + last_sign_date = pytz.timezone(self.timezone).localize(last_sign_date) + + if last_sign_date == yesterday: + streak = user_record['signin_streak'] + 1 + + # 计算积分 + points = self.calculate_points(streak) + return points + + # 修改process_message方法,使用装饰器 @plugin_stats_decorator(plugin_name="签到系统") + @points_reward_decorator(calculate_sign_in_points, "checkin", "每日签到奖励") def process_message(self, message: Dict[str, Any]) -> Tuple[bool, Optional[str]]: """处理消息""" content = str(message.get("content", "")).strip() @@ -156,31 +199,27 @@ class MessageSignPlugin(MessagePluginInterface): try: # 获取当前时间,带有时区信息 current_time = datetime.now(tz=pytz.timezone(self.timezone)) - - # 获取当天零点的时间 today_start = current_time.replace(hour=0, minute=0, second=0, microsecond=0) - - # 获取昨天的时间 yesterday = today_start - timedelta(days=1) - + # 获取用户的签到记录 user_record = self.get_user_record(sender, roomid) wx_nick_name = all_contacts.get(sender, sender) - + # 判断用户是否已经签到过 if user_record and user_record.get('sign_stat'): sign_stat = user_record['sign_stat'] - + # 确保 sign_stat 和 today_start 是同一时区对象 if isinstance(sign_stat, datetime) and sign_stat.tzinfo is None: sign_stat = pytz.timezone(self.timezone).localize(sign_stat) - + # 如果 sign_stat 已经大于或等于今天的零点,则认为用户已经签到过了 if sign_stat >= today_start: self.message_util.send_text_msg(f"您今天已经签到过了!当前积分:{user_record['points']}", (roomid if roomid else sender), sender) return True, "已签到" - + streak = 0 streak_broken = False old_streak = 1 @@ -200,28 +239,29 @@ class MessageSignPlugin(MessagePluginInterface): streak_broken = True else: streak = 1 - + today_signin_rank = self.get_today_signin_count(roomid) + 1 self.today_signin_count[roomid] = today_signin_rank self.sign_in_redis.save_signin_count(roomid, today_signin_rank) - - points_to_add = self.calculate_points(streak) - + # 使用数据库操作类更新或创建签到记录 + # 注意:不再在这里计算和添加积分,由装饰器处理 + points_to_add = self.calculate_points(streak) if user_record: self.sign_in_db.update_sign_record( sender, roomid, wx_nick_name, - points_to_add, current_time, streak + points_to_add, # 不在这里添加积分,由装饰器处理 + current_time, streak ) else: self.sign_in_db.create_sign_record( sender, roomid, wx_nick_name, - points_to_add, current_time, streak + points_to_add, # 不在这里添加积分,由装饰器处理 + current_time, streak ) - - # 在签到成功后添加每日词汇 + # 在输出信息中添加每日词汇 - output = f"签到成功,加[{points_to_add}]分,第[{today_signin_rank}]个!" + output = f"签到成功,第[{today_signin_rank}]个!" if streak_broken and old_streak > 0: # 只有在真的断签且之前有签到记录时才显示 output += f"断开了 {old_streak} 天连签!" @@ -239,7 +279,7 @@ class MessageSignPlugin(MessagePluginInterface): self.LOG.error(f"处理签到请求出错: {e}") self.message_util.send_text_msg(f"签到出错:{e}", (roomid if roomid else sender), sender) - return True, f"处理出错: {e}" + return False, f"处理出错: {e}" def reset_today_count_if_needed(self): """检查并重置每日签到计数""" diff --git a/plugins/message_summary/main.py b/plugins/message_summary/main.py index bb93ed7..f607b6e 100644 --- a/plugins/message_summary/main.py +++ b/plugins/message_summary/main.py @@ -4,13 +4,14 @@ from typing import Dict, Any, Tuple, Optional, List import requests -from message_storage.message_to_db import MessageStorage +from utils.wechat.message_to_db import MessageStorage from utils.compress_chat_data import compress_chat_data from plugin_common.message_plugin_interface import MessagePluginInterface from plugin_common.plugin_interface import PluginStatus +from utils.decorator.points_decorator import plugin_points_cost from utils.markdown_to_image import convert_md_str_to_image -from plugins.stats_collector.decorators import plugin_stats_decorator -from robot_cmd.robot_command import GroupBotManager, Feature, PermissionStatus +from utils.decorator.plugin_decorators import plugin_stats_decorator +from utils.robot_cmd.robot_command import GroupBotManager, Feature, PermissionStatus class MessageSummaryPlugin(MessagePluginInterface): @@ -73,6 +74,7 @@ class MessageSummaryPlugin(MessagePluginInterface): return True @plugin_stats_decorator(plugin_name="群聊总结") + @plugin_points_cost(10, "群聊总结消耗积分") def process_message(self, message: Dict[str, Any]) -> Tuple[bool, Optional[str]]: """处理消息""" try: diff --git a/plugins/music/main.py b/plugins/music/main.py index c56ec0a..6cd2863 100644 --- a/plugins/music/main.py +++ b/plugins/music/main.py @@ -7,8 +7,9 @@ from wcferry import Wcf from plugin_common.message_plugin_interface import MessagePluginInterface from plugin_common.plugin_interface import PluginStatus -from plugins.stats_collector.decorators import plugin_stats_decorator -from robot_cmd.robot_command import Feature, PermissionStatus, GroupBotManager +from utils.decorator.plugin_decorators import plugin_stats_decorator +from utils.robot_cmd.robot_command import Feature, PermissionStatus, GroupBotManager +from utils.decorator.points_decorator import plugin_points_cost class MusicPlugin(MessagePluginInterface): @@ -81,6 +82,7 @@ class MusicPlugin(MessagePluginInterface): return command in self._commands @plugin_stats_decorator(plugin_name="音乐点播") + @plugin_points_cost(1, "音乐点播消耗积分") def process_message(self, message: Dict[str, Any]) -> Tuple[bool, Optional[str]]: """处理消息""" content = str(message.get("content", "")).strip() diff --git a/plugins/plugin_manager/main.py b/plugins/plugin_manager/main.py index b3df894..81e1008 100644 --- a/plugins/plugin_manager/main.py +++ b/plugins/plugin_manager/main.py @@ -6,7 +6,7 @@ from plugin_common.message_plugin_interface import MessagePluginInterface from plugin_common.plugin_interface import PluginStatus from plugin_common.plugin_registry import PluginRegistry from plugin_common.plugin_manager import PluginManager -from robot_cmd.robot_command import GroupBotManager +from utils.robot_cmd.robot_command import GroupBotManager class PluginManagerPlugin(MessagePluginInterface): diff --git a/plugins/point_trade/main.py b/plugins/point_trade/main.py index 71efa95..137c903 100644 --- a/plugins/point_trade/main.py +++ b/plugins/point_trade/main.py @@ -1,7 +1,5 @@ import logging import re -import os -import toml from datetime import datetime from typing import Dict, Any, List, Optional, Tuple import xml.etree.ElementTree as ET @@ -11,8 +9,8 @@ from wcferry import Wcf from db.connection import DBConnectionManager from plugin_common.message_plugin_interface import MessagePluginInterface from plugin_common.plugin_interface import PluginStatus -from plugins.stats_collector.decorators import plugin_stats_decorator -from robot_cmd.robot_command import Feature, PermissionStatus, GroupBotManager +from utils.decorator.plugin_decorators import plugin_stats_decorator +from utils.robot_cmd.robot_command import Feature, PermissionStatus, GroupBotManager import mysql.connector.pooling diff --git a/plugins/system_updater/main.py b/plugins/system_updater/main.py index 143acaa..417f2b7 100644 --- a/plugins/system_updater/main.py +++ b/plugins/system_updater/main.py @@ -6,7 +6,7 @@ from typing import Dict, Any, List, Optional, Tuple from plugin_common.message_plugin_interface import MessagePluginInterface from plugin_common.plugin_interface import PluginStatus -from robot_cmd.robot_command import Feature, PermissionStatus, GroupBotManager +from utils.robot_cmd.robot_command import Feature, PermissionStatus # 动态导入win_click.py diff --git a/plugins/video/main.py b/plugins/video/main.py index e0c2a0c..cc14aa5 100644 --- a/plugins/video/main.py +++ b/plugins/video/main.py @@ -7,8 +7,9 @@ from wcferry import Wcf from plugin_common.message_plugin_interface import MessagePluginInterface from plugin_common.plugin_interface import PluginStatus -from plugins.stats_collector.decorators import plugin_stats_decorator -from robot_cmd.robot_command import Feature, PermissionStatus, GroupBotManager +from utils.decorator.plugin_decorators import plugin_stats_decorator +from utils.robot_cmd.robot_command import Feature, PermissionStatus, GroupBotManager +from utils.decorator.points_decorator import plugin_points_cost class VideoPlugin(MessagePluginInterface): @@ -88,6 +89,7 @@ class VideoPlugin(MessagePluginInterface): return command in self._commands @plugin_stats_decorator(plugin_name="视频插件") + @plugin_points_cost(1, "视频插件消耗积分") def process_message(self, message: Dict[str, Any]) -> Tuple[bool, Optional[str]]: """处理消息""" content = str(message.get("content", "")).strip() diff --git a/plugins/video_man/main.py b/plugins/video_man/main.py index 7c2ec5c..683bbd1 100644 --- a/plugins/video_man/main.py +++ b/plugins/video_man/main.py @@ -8,8 +8,9 @@ from wcferry import Wcf from message_util import MessageUtil from plugin_common.message_plugin_interface import MessagePluginInterface from plugin_common.plugin_interface import PluginStatus -from plugins.stats_collector.decorators import plugin_stats_decorator -from robot_cmd.robot_command import Feature, PermissionStatus, GroupBotManager +from utils.decorator.plugin_decorators import plugin_stats_decorator +from utils.robot_cmd.robot_command import Feature, PermissionStatus, GroupBotManager +from utils.decorator.points_decorator import plugin_points_cost class VideoManPlugin(MessagePluginInterface): @@ -89,6 +90,7 @@ class VideoManPlugin(MessagePluginInterface): return command in self._commands @plugin_stats_decorator(plugin_name="猛男视频") + @plugin_points_cost(1, "猛男视频消耗积分") def process_message(self, message: Dict[str, Any]) -> Tuple[bool, Optional[str]]: """处理消息""" content = str(message.get("content", "")).strip() diff --git a/plugins/xiuren_image/main.py b/plugins/xiuren_image/main.py index 488ccc9..bf699d1 100644 --- a/plugins/xiuren_image/main.py +++ b/plugins/xiuren_image/main.py @@ -7,8 +7,9 @@ from wcferry import Wcf from plugin_common.message_plugin_interface import MessagePluginInterface from plugin_common.plugin_interface import PluginStatus -from plugins.stats_collector.decorators import plugin_stats_decorator -from robot_cmd.robot_command import Feature, PermissionStatus, GroupBotManager +from utils.decorator.plugin_decorators import plugin_stats_decorator +from utils.robot_cmd.robot_command import Feature, PermissionStatus, GroupBotManager +from utils.decorator.points_decorator import plugin_points_cost class XiurenImagePlugin(MessagePluginInterface): @@ -87,6 +88,7 @@ class XiurenImagePlugin(MessagePluginInterface): return command in self._commands @plugin_stats_decorator(plugin_name="秀人图片") + @plugin_points_cost(1, "秀人图片消耗积分") def process_message(self, message: Dict[str, Any]) -> Tuple[bool, Optional[str]]: """处理消息""" content = str(message.get("content", "")).strip() diff --git a/robot.py b/robot.py index a7373f9..a242246 100644 --- a/robot.py +++ b/robot.py @@ -22,17 +22,16 @@ from base.func_xinghuo_web import XinghuoWeb from base.func_claude import Claude from configuration import Config from constants import ChatType -from game_task.game_task_encyclopedia import game_process_message, get_group_ids, run_random_task_assignment -from message_storage.message_to_db import MessageStorage +from utils.wechat.message_to_db import MessageStorage from plugin_common.event_system import EventType, EventSystem from plugin_common.message_plugin_interface import MessagePluginInterface from plugin_common.plugin_interface import PluginStatus from plugin_common.plugin_manager import PluginManager from plugin_common.plugin_registry import PluginRegistry -from robot_cmd.robot_command import GroupBotManager +from utils.robot_cmd.robot_command import GroupBotManager from job_mgmt import Job -from robot_cmd.robot_command import Feature -from robot_cmd.robot_command import PermissionStatus +from utils.robot_cmd.robot_command import Feature +from utils.robot_cmd.robot_command import PermissionStatus __version__ = "39.2.4.0" @@ -243,26 +242,6 @@ class Robot(Job): except Exception as e: self.LOG.error(f"revoke_receive_message error: {e}") - # 兼容不@ 直接/触发指令,回答问题。 - try: - if msg.content.startswith("/"): - # 进行权限判断 加入权限,防止tokens浪费 - if self.gbm.get_group_permission(msg.roomid, Feature.TASK_GAME) == PermissionStatus.DISABLED: - return - else: - # 因为内容中存在空格指令,所以不能使用 - self.LOG.info(f"msg.content:{msg.content}\n game_message: {msg.content}") - resp = game_process_message(group_id=msg.roomid, player_id=msg.sender, message=msg.content, - player_name=self.allContacts.get(msg.sender, msg.sender)) - message = resp["message"] - player_id = resp["player_id"] - print(f"消息: {message}") - print(f"玩家ID: {player_id}") - self.send_text_msg(message, msg.roomid, msg.sender) - return - except Exception as e: - self.LOG.error(f"game_message_load error:{e}") - if msg.is_at(self.wxid): # 被@ self.toAt(msg) return # 处理完群聊信息,后面就不需要处理了 @@ -511,21 +490,21 @@ class Robot(Job): self.send_text_msg(output, r) except Exception as e: self.LOG.error(f"SendRanking error:{e}") - - # 设置定时任务 - def game_auto_tasks(self): - try: - group_ids = get_group_ids() - for gid in group_ids: - if self.gbm.get_group_permission(gid, Feature.TASK_GAME) == PermissionStatus.ENABLED: - rep = run_random_task_assignment(group_id=gid) - message = rep["message"] - player_id = rep["player_id"] - print(f"消息: {message}") - print(f"玩家ID: {player_id}") - self.send_text_msg(message, gid, player_id) - except Exception as e: - self.LOG.error(f"message_summary_robot error:{e}") + # + # # 设置定时任务 + # def game_auto_tasks(self): + # try: + # group_ids = get_group_ids() + # for gid in group_ids: + # if self.gbm.get_group_permission(gid, Feature.TASK_GAME) == PermissionStatus.ENABLED: + # rep = run_random_task_assignment(group_id=gid) + # message = rep["message"] + # player_id = rep["player_id"] + # print(f"消息: {message}") + # print(f"玩家ID: {player_id}") + # self.send_text_msg(message, gid, player_id) + # except Exception as e: + # self.LOG.error(f"message_summary_robot error:{e}") def xiu_ren_download_task(self): try: diff --git a/game_task/game_chatgpt_qa.py b/utils/ai/game_chatgpt_qa.py similarity index 100% rename from game_task/game_chatgpt_qa.py rename to utils/ai/game_chatgpt_qa.py diff --git a/plugins/stats_collector/decorators.py b/utils/decorator/plugin_decorators.py similarity index 100% rename from plugins/stats_collector/decorators.py rename to utils/decorator/plugin_decorators.py diff --git a/utils/decorator/points_decorator.py b/utils/decorator/points_decorator.py new file mode 100644 index 0000000..530574d --- /dev/null +++ b/utils/decorator/points_decorator.py @@ -0,0 +1,164 @@ +import functools +import time +import traceback +import logging +from datetime import datetime +from typing import Callable, Dict, Any, Tuple, Union + +from db.connection import DBConnectionManager +from db.points_db import PointsDBOperator, PointSource + + +def points_reward_decorator(points_calculator: Union[int, Callable], source_type: str = "other", + description: str = None): + """积分奖励装饰器 + + Args: + points_calculator: 积分数量或计算函数,如果是函数,接收(self, message, success, response)参数并返回积分数量 + source_type: 积分来源类型 (checkin, game, other) + description: 积分奖励描述 + + Returns: + 装饰器函数 + """ + + def decorator(func: Callable) -> Callable: + @functools.wraps(func) + def wrapper(self, message: Dict[str, Any]) -> Tuple[bool, str]: + # 调用原始方法 + success, response = func(self, message) + + # 如果原始方法执行成功,奖励积分 + if success: + try: + # 获取消息信息 + sender = message.get("sender", "") + roomid = message.get("roomid", "") + + if sender and (roomid or sender): + # 计算奖励积分数量 + if callable(points_calculator): + # 如果是函数,调用函数计算积分 + points = points_calculator(self, message, success, response) + if not points or points <= 0: + # 如果计算结果为0或负数,不奖励积分 + return success, response + else: + # 如果是固定值,直接使用 + points = points_calculator + + # 获取积分来源类型 + source = PointSource.CHECKIN + if source_type.lower() == "game": + source = PointSource.GAME + elif source_type.lower() != "checkin": + source = PointSource.OTHER + + # 奖励积分 + db_manager = DBConnectionManager.get_instance() + points_db = PointsDBOperator(db_manager) + + # 如果description是函数,调用函数获取描述 + desc = description + if callable(description): + desc = description(self, message, success, response, points) + + reward_success, reward_result = points_db.add_points( + sender, roomid, points, source, + desc or f"使用 {self.name if hasattr(self, 'name') else '功能'} 获得奖励" + ) + + logger = logging.getLogger(f"PointsReward.{self.name if hasattr(self, 'name') else 'Unknown'}") + if reward_success: + logger.info(f"用户 {sender} 获得 {points} 积分奖励") + + # 如果响应中没有提到积分,添加积分信息 + if "积分" not in response: + response += f"\n\n🎁 恭喜获得 {points} 积分奖励!" + else: + logger.warning(f"用户 {sender} 积分奖励失败: {reward_result}") + except Exception as e: + logger = logging.getLogger("PointsReward") + logger.error(f"奖励积分失败: {e}") + logger.error(traceback.format_exc()) + + return success, response + + return wrapper + + return decorator + + +def plugin_points_cost(points: int, description: str = None): + """插件积分消费装饰器 + + Args: + points: 消费积分数量 + description: 积分消费描述 + + Returns: + 装饰器函数 + """ + + def decorator(func: Callable) -> Callable: + @functools.wraps(func) + def wrapper(self, message: Dict[str, Any]) -> Tuple[bool, str]: + try: + # 获取消息信息 + sender = message.get("sender", "") + roomid = message.get("roomid", "") + + if not sender or not (roomid or sender): + return func(self, message) + + # 检查用户积分是否足够 + db_manager = DBConnectionManager.get_instance() + points_db = PointsDBOperator(db_manager) + + plugin_name = self.name if hasattr(self, 'name') else "未知插件" + logger = logging.getLogger(f"PointsCost.{plugin_name}") + + user_points = points_db.get_user_points(sender, roomid) + if user_points["total_points"] < points: + # 积分不足 + wcf = message.get("wcf") + if wcf: + wcf.send_text( + f"❌ 积分不足,无法使用 {plugin_name} 功能\n" + f"当前积分: {user_points['total_points']}\n" + f"需要积分: {points}\n" + f"还差 {points - user_points['total_points']} 积分", + (roomid if roomid else sender), sender + ) + logger.info(f"用户 {sender} 积分不足,无法使用功能") + return False, "积分不足" + + # 调用原始方法 + success, response = func(self, message) + + # 如果原始方法执行成功,扣除积分 + if success: + deduct_success, deduct_result = points_db.deduct_points( + sender, roomid, points, PointSource.PLUGIN, + description or f"使用 {plugin_name} 功能" + ) + + if deduct_success: + logger.info(f"用户 {sender} 使用功能扣除 {points} 积分") + + # 如果响应中没有提到积分,添加积分信息 + if "积分" not in response: + response += f"\n\n💰 已消费 {points} 积分" + else: + logger.warning(f"用户 {sender} 积分扣除失败: {deduct_result}") + + return success, response + except Exception as e: + logger = logging.getLogger("PointsCost") + logger.error(f"积分消费失败: {e}") + logger.error(traceback.format_exc()) + return func(self, message) + + return wrapper + + return decorator diff --git a/robot_cmd/robot_command.py b/utils/robot_cmd/robot_command.py similarity index 100% rename from robot_cmd/robot_command.py rename to utils/robot_cmd/robot_command.py diff --git a/message_storage/message_to_db.py b/utils/wechat/message_to_db.py similarity index 99% rename from message_storage/message_to_db.py rename to utils/wechat/message_to_db.py index 229d4e9..8ecfc3a 100644 --- a/message_storage/message_to_db.py +++ b/utils/wechat/message_to_db.py @@ -3,9 +3,6 @@ import xml.etree.ElementTree as ET import logging import concurrent.futures # 添加线程池支持 import os -import hashlib -import shutil - from wcferry import WxMsg, Wcf from db.connection import DBConnectionManager diff --git a/xiuren/main.py b/xiuren/main.py index b996d7e..a728a2c 100644 --- a/xiuren/main.py +++ b/xiuren/main.py @@ -3,7 +3,7 @@ import tomllib from wcferry import WxMsg, Wcf -from robot_cmd.robot_command import Feature, PermissionStatus, GroupBotManager +from utils.robot_cmd.robot_command import Feature, PermissionStatus, GroupBotManager from xiuren.random_pic import get_xiuren_pic