需求:1.加入了用户积分表;2.加入了指令积分扣除功能;3.加入了积分获得与扣除注解。

This commit is contained in:
liuwei
2025-04-09 11:54:03 +08:00
parent 9b8e0c3558
commit dba9c31504
31 changed files with 1435 additions and 527 deletions

View File

@@ -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
# 创建机器人管理蓝图

578
db/points_db.py Normal file
View File

@@ -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

View File

@@ -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")) # 查看排行榜

View File

@@ -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()

View File

@@ -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()

View File

@@ -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):

View File

@@ -0,0 +1,5 @@
from .main import GameTaskPlugin
def get_plugin():
"""获取插件实例"""
return GameTaskPlugin()

View File

@@ -0,0 +1,8 @@
enable = true
command = ["game", "任务", "/t", "/a", "/s"]
command-format = """
🎮 游戏任务系统指令:
/s - 加入游戏
/t - 获取任务
/a <任务ID> <答案> - 提交答案
"""

552
plugins/game_task/main.py Normal file
View File

@@ -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}")

View File

@@ -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):

View File

@@ -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):

View File

@@ -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 # 导入消息工具类

View File

@@ -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

View File

@@ -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):
"""检查并重置每日签到计数"""

View File

@@ -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:

View File

@@ -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()

View File

@@ -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):

View File

@@ -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

View File

@@ -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

View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

View File

@@ -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:

View File

@@ -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

View File

@@ -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

View File

@@ -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