From 5b0a703b46221b8363a84cf83baa5c2c9e9cb558 Mon Sep 17 00:00:00 2001 From: liuwei Date: Fri, 18 Apr 2025 12:05:16 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B0=83=E6=95=B4=20message=5Futil=20=E7=9A=84?= =?UTF-8?q?=E5=8F=91=E9=80=81text=E6=96=B9=E6=B3=95=E5=90=8D=EF=BC=8C?= =?UTF-8?q?=E6=96=B9=E4=BE=BF=E6=9B=BF=E6=8D=A2wcf.send=5Ftext=E3=80=82?= =?UTF-8?q?=E5=8A=A0=E5=85=A5=E4=BA=86=E8=99=9A=E6=8B=9F=E7=BE=A4=E7=BB=84?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E5=8A=9F=E8=83=BD=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- admin/dashboard/blueprints/virtual_group.py | 282 ++++++++++++++ admin/dashboard/server.py | 7 +- admin/dashboard/templates/base.html | 8 +- .../templates/virtual_group_management.html | 364 ++++++++++++++++++ db/group_virtual_redis.py | 57 +++ message_util.py | 24 +- plugins/beautyleg/main.py | 4 +- plugins/dify/main.py | 22 +- plugins/game_task/main.py | 58 +-- plugins/group_member_change/main.py | 2 +- plugins/group_virtual/README.md | 141 +++++++ plugins/group_virtual/__init__.py | 7 + plugins/group_virtual/config.toml | 3 + plugins/group_virtual/main.py | 184 +++++++++ plugins/message_sign/main.py | 26 +- plugins/point_trade/main.py | 94 ++--- plugins/video_man/main.py | 4 +- robot.py | 2 +- 18 files changed, 1173 insertions(+), 116 deletions(-) create mode 100644 admin/dashboard/blueprints/virtual_group.py create mode 100644 admin/dashboard/templates/virtual_group_management.html create mode 100644 db/group_virtual_redis.py create mode 100644 plugins/group_virtual/README.md create mode 100644 plugins/group_virtual/__init__.py create mode 100644 plugins/group_virtual/config.toml create mode 100644 plugins/group_virtual/main.py diff --git a/admin/dashboard/blueprints/virtual_group.py b/admin/dashboard/blueprints/virtual_group.py new file mode 100644 index 0000000..6fde1c3 --- /dev/null +++ b/admin/dashboard/blueprints/virtual_group.py @@ -0,0 +1,282 @@ +import logging +import uuid +from flask import Blueprint, jsonify, request, current_app, render_template +from .auth import login_required + +logger = logging.getLogger(__name__) +virtual_group_bp = Blueprint('virtual_group', __name__) + +# 添加虚拟群组管理视图 +@virtual_group_bp.route('/virtual_group') +@login_required +def virtual_group(): + return render_template('virtual_group_management.html') + +@virtual_group_bp.route('/api/virtual_groups', methods=['GET']) +@login_required +def get_virtual_groups(): + """获取所有虚拟群组""" + try: + server = current_app.dashboard_server + redis_client = server.db_manager.get_redis_connection() + + # 从Redis获取虚拟群组数据 + chat_groups_json = redis_client.get("group_virtual:chat_groups") + if not chat_groups_json: + return jsonify({"success": True, "data": {"chatGroups": []}}) + + import json + chat_groups = json.loads(chat_groups_json) + + # 确保群组名称是最新的 + for chat_group in chat_groups.get("chatGroups", []): + for group in chat_group.get("groups", []): + group_id = group.get("id") + if group_id: + group_name = server.contact_manager.get_nickname(group_id) + if group_name: + group["name"] = group_name + + return jsonify({"success": True, "data": chat_groups}) + except Exception as e: + logger.error(f"获取虚拟群组失败: {e}") + return jsonify({"success": False, "error": str(e)}), 500 + + +@virtual_group_bp.route('/api/virtual_groups', methods=['POST']) +@login_required +def create_virtual_group(): + """创建新的虚拟群组""" + try: + server = current_app.dashboard_server + redis_client = server.db_manager.get_redis_connection() + data = request.json + + group_name = data.get('name') + if not group_name: + return jsonify({"success": False, "error": "群组名称不能为空"}), 400 + + # 从Redis获取现有虚拟群组 + import json + chat_groups_json = redis_client.get("group_virtual:chat_groups") + if chat_groups_json: + chat_groups = json.loads(chat_groups_json) + else: + chat_groups = {"chatGroups": []} + + # 创建新的虚拟群组 + new_group = { + "id": str(uuid.uuid4()), + "name": group_name, + "groups": [] + } + + chat_groups["chatGroups"].append(new_group) + + # 保存回Redis + redis_client.set("group_virtual:chat_groups", json.dumps(chat_groups, ensure_ascii=False)) + + return jsonify({ + "success": True, + "message": f"虚拟群组 {group_name} 创建成功", + "data": new_group + }) + except Exception as e: + logger.error(f"创建虚拟群组失败: {e}") + return jsonify({"success": False, "error": str(e)}), 500 + + +@virtual_group_bp.route('/api/virtual_groups/', methods=['DELETE']) +@login_required +def delete_virtual_group(group_id): + """删除虚拟群组""" + try: + server = current_app.dashboard_server + redis_client = server.db_manager.get_redis_connection() + + # 从Redis获取现有虚拟群组 + import json + chat_groups_json = redis_client.get("group_virtual:chat_groups") + if not chat_groups_json: + return jsonify({"success": False, "error": "虚拟群组不存在"}), 404 + + chat_groups = json.loads(chat_groups_json) + + # 查找并删除指定的虚拟群组 + found = False + for i, group in enumerate(chat_groups.get("chatGroups", [])): + if group.get("id") == group_id: + del chat_groups["chatGroups"][i] + found = True + break + + if not found: + return jsonify({"success": False, "error": "虚拟群组不存在"}), 404 + + # 保存回Redis + redis_client.set("group_virtual:chat_groups", json.dumps(chat_groups, ensure_ascii=False)) + + return jsonify({ + "success": True, + "message": "虚拟群组删除成功" + }) + except Exception as e: + logger.error(f"删除虚拟群组失败: {e}") + return jsonify({"success": False, "error": str(e)}), 500 + + +@virtual_group_bp.route('/api/virtual_groups//groups', methods=['POST']) +@login_required +def add_group_to_virtual(group_id): + """向虚拟群组添加微信群""" + try: + server = current_app.dashboard_server + redis_client = server.db_manager.get_redis_connection() + data = request.json + + wx_group_id = data.get('wx_group_id') + if not wx_group_id: + return jsonify({"success": False, "error": "微信群ID不能为空"}), 400 + + # 检查微信群是否存在 + group_name = server.contact_manager.get_nickname(wx_group_id) + if not group_name: + return jsonify({"success": False, "error": "微信群不存在或未被机器人识别"}), 404 + + # 从Redis获取现有虚拟群组 + import json + chat_groups_json = redis_client.get("group_virtual:chat_groups") + if not chat_groups_json: + return jsonify({"success": False, "error": "虚拟群组不存在"}), 404 + + chat_groups = json.loads(chat_groups_json) + + # 查找指定的虚拟群组 + found = False + for chat_group in chat_groups.get("chatGroups", []): + if chat_group.get("id") == group_id: + # 检查群是否已在虚拟群组中 + for group in chat_group.get("groups", []): + if group.get("id") == wx_group_id: + return jsonify({"success": False, "error": "该微信群已在虚拟群组中"}), 400 + + # 添加微信群到虚拟群组 + chat_group["groups"].append({ + "id": wx_group_id, + "name": group_name + }) + found = True + break + + if not found: + return jsonify({"success": False, "error": "虚拟群组不存在"}), 404 + + # 保存回Redis + redis_client.set("group_virtual:chat_groups", json.dumps(chat_groups, ensure_ascii=False)) + + return jsonify({ + "success": True, + "message": f"微信群 {group_name} 已添加到虚拟群组", + "data": { + "id": wx_group_id, + "name": group_name + } + }) + except Exception as e: + logger.error(f"向虚拟群组添加微信群失败: {e}") + return jsonify({"success": False, "error": str(e)}), 500 + + +@virtual_group_bp.route('/api/virtual_groups//groups/', methods=['DELETE']) +@login_required +def remove_group_from_virtual(group_id, wx_group_id): + """从虚拟群组移除微信群""" + try: + server = current_app.dashboard_server + redis_client = server.db_manager.get_redis_connection() + + # 从Redis获取现有虚拟群组 + import json + chat_groups_json = redis_client.get("group_virtual:chat_groups") + if not chat_groups_json: + return jsonify({"success": False, "error": "虚拟群组不存在"}), 404 + + chat_groups = json.loads(chat_groups_json) + + # 查找指定的虚拟群组和微信群 + found_group = False + found_wx_group = False + for chat_group in chat_groups.get("chatGroups", []): + if chat_group.get("id") == group_id: + found_group = True + for i, group in enumerate(chat_group.get("groups", [])): + if group.get("id") == wx_group_id: + del chat_group["groups"][i] + found_wx_group = True + break + break + + if not found_group: + return jsonify({"success": False, "error": "虚拟群组不存在"}), 404 + + if not found_wx_group: + return jsonify({"success": False, "error": "该微信群不在虚拟群组中"}), 404 + + # 保存回Redis + redis_client.set("group_virtual:chat_groups", json.dumps(chat_groups, ensure_ascii=False)) + + return jsonify({ + "success": True, + "message": "微信群已从虚拟群组移除" + }) + except Exception as e: + logger.error(f"从虚拟群组移除微信群失败: {e}") + return jsonify({"success": False, "error": str(e)}), 500 + + +@virtual_group_bp.route('/api/virtual_groups/', methods=['PUT']) +@login_required +def update_virtual_group(group_id): + """更新虚拟群组信息""" + try: + server = current_app.dashboard_server + redis_client = server.db_manager.get_redis_connection() + data = request.json + + group_name = data.get('name') + if not group_name: + return jsonify({"success": False, "error": "群组名称不能为空"}), 400 + + # 从Redis获取现有虚拟群组 + import json + chat_groups_json = redis_client.get("group_virtual:chat_groups") + if not chat_groups_json: + return jsonify({"success": False, "error": "虚拟群组不存在"}), 404 + + chat_groups = json.loads(chat_groups_json) + + # 查找并更新指定的虚拟群组 + found = False + for chat_group in chat_groups.get("chatGroups", []): + if chat_group.get("id") == group_id: + chat_group["name"] = group_name + found = True + break + + if not found: + return jsonify({"success": False, "error": "虚拟群组不存在"}), 404 + + # 保存回Redis + redis_client.set("group_virtual:chat_groups", json.dumps(chat_groups, ensure_ascii=False)) + + return jsonify({ + "success": True, + "message": "虚拟群组更新成功", + "data": { + "id": group_id, + "name": group_name + } + }) + except Exception as e: + logger.error(f"更新虚拟群组失败: {e}") + return jsonify({"success": False, "error": str(e)}), 500 \ No newline at end of file diff --git a/admin/dashboard/server.py b/admin/dashboard/server.py index 0a566ad..80541f2 100644 --- a/admin/dashboard/server.py +++ b/admin/dashboard/server.py @@ -128,11 +128,10 @@ class DashboardServer: from admin.dashboard.blueprints.system import system_bp from admin.dashboard.blueprints.main import main_bp from admin.dashboard.blueprints.plugin_routes import plugin_routes + from blueprints.virtual_group import virtual_group_bp - # 将服务器实例存储在应用上下文中 - app.dashboard_server = self - - # 注册蓝图 + # 在app.register_blueprint部分添加 + app.register_blueprint(virtual_group_bp, url_prefix='/virtual_group') app.register_blueprint(auth_bp) app.register_blueprint(main_bp) app.register_blueprint(contacts_bp) diff --git a/admin/dashboard/templates/base.html b/admin/dashboard/templates/base.html index 963216b..6235841 100644 --- a/admin/dashboard/templates/base.html +++ b/admin/dashboard/templates/base.html @@ -166,6 +166,11 @@ 插件管理 + + + + 虚拟群组管理 + @@ -229,7 +234,8 @@ '7': '/messages', '9': '/wx_logs', '10': '/contacts', - '11': '/plugins_manage' + '11': '/plugins_manage', + '12': '/virtual_group' }; // 如果当前不在对应页面,则跳转 diff --git a/admin/dashboard/templates/virtual_group_management.html b/admin/dashboard/templates/virtual_group_management.html new file mode 100644 index 0000000..f9df6b2 --- /dev/null +++ b/admin/dashboard/templates/virtual_group_management.html @@ -0,0 +1,364 @@ +{% extends "base.html" %} + +{% block title %}虚拟群组管理 - 机器人管理后台{% endblock %} + +{% block content %} + +
+ + + +
+ 虚拟群组管理 + + 创建虚拟群组 + + + +
+ + + + + + + + + + + + + + +
+
+
+ + + + + + + + + + 取消 + 确定 + + + + + + + + + + + + 取消 + 确定 + + + + + + + + + + {% raw %}{{ group.name }}{% endraw %} + {% raw %}{{ group.wxid }}{% endraw %} + + + + + + 取消 + 确定 + + +
+{% endblock %} + +{% block scripts %} + +{% endblock %} \ No newline at end of file diff --git a/db/group_virtual_redis.py b/db/group_virtual_redis.py new file mode 100644 index 0000000..bc252e2 --- /dev/null +++ b/db/group_virtual_redis.py @@ -0,0 +1,57 @@ +import json +import logging +from typing import Dict, Any, List, Optional + +from db.connection import DBConnectionManager + + +class GroupVirtualRedisDB: + """群组虚拟聊天Redis数据库操作类""" + + def __init__(self, db_manager: DBConnectionManager): + self.db_manager = db_manager + self.redis_client = db_manager.get_redis_connection() + self.LOG = logging.getLogger("Plugin.GroupVirtual.Redis") + self.chat_groups_key = "group_virtual:chat_groups" + + def load_chat_groups(self) -> Dict[str, Any]: + """从Redis加载虚拟聊天组数据""" + try: + chat_groups_json = self.redis_client.get(self.chat_groups_key) + if chat_groups_json: + return json.loads(chat_groups_json) + else: + # 如果Redis中没有数据,返回空结构 + return {"chatGroups": []} + except Exception as e: + self.LOG.error(f"从Redis加载虚拟聊天组数据失败: {e}") + return {"chatGroups": []} + + def save_chat_groups(self, chat_groups: Dict[str, Any]) -> bool: + """保存虚拟聊天组数据到Redis""" + try: + chat_groups_json = json.dumps(chat_groups, ensure_ascii=False) + self.redis_client.set(self.chat_groups_key, chat_groups_json) + return True + except Exception as e: + self.LOG.error(f"保存虚拟聊天组数据到Redis失败: {e}") + return False + + def get_chat_group(self, chat_group_id: str) -> Optional[Dict[str, Any]]: + """获取指定ID的虚拟聊天组""" + chat_groups = self.load_chat_groups() + for group in chat_groups.get("chatGroups", []): + if group["id"] == chat_group_id: + return group + return None + + def get_chat_groups_by_group_id(self, group_id: str) -> List[Dict[str, Any]]: + """获取包含指定群的所有虚拟聊天组""" + result = [] + chat_groups = self.load_chat_groups() + for chat_group in chat_groups.get("chatGroups", []): + for group in chat_group.get("groups", []): + if group["id"] == group_id: + result.append(chat_group) + break + return result \ No newline at end of file diff --git a/message_util.py b/message_util.py index c9ac776..057f266 100644 --- a/message_util.py +++ b/message_util.py @@ -18,7 +18,7 @@ class MessageUtil: self.contact_manager = contact_manager self.LOG = logging.getLogger("MessageUtil") - def send_text_msg(self, msg: str, receiver: str, at_list: str = "") -> None: + def send_text(self, msg: str, receiver: str, at_list: str = "") -> None: """ 发送文本消息 @@ -56,11 +56,25 @@ class MessageUtil: """ # 风控处理,随机延迟发送,解决群消息高频发送导致的微信风险 time.sleep(random.uniform(0.5, 1.5)) - + self.LOG.info(f"Sending file to {receiver}: {file_path}") self.wcf.send_file(file_path, receiver) - def send_rich_text(self, name: str, account: str, title: str, digest: str, url: str, thumburl: str, receiver: str) -> int: + def send_image(self, image_path: str, receiver: str) -> None: + """ + 发送文件消息 + + :param image_path: 文件路径 + :param receiver: 接收人wxid或者群id + """ + # 风控处理,随机延迟发送,解决群消息高频发送导致的微信风险 + time.sleep(random.uniform(0.5, 1.5)) + + self.LOG.info(f"Sending file to {receiver}: {image_path}") + self.wcf.send_image(image_path, receiver) + + def send_rich_text(self, name: str, account: str, title: str, digest: str, url: str, thumburl: str, + receiver: str) -> int: """ 发送富文本消息 @@ -85,7 +99,7 @@ class MessageUtil: """ # 风控处理,随机延迟发送,解决群消息高频发送导致的微信风险 time.sleep(random.uniform(0.5, 1.5)) - + self.LOG.info(f"Sending rich text to {receiver}: {title}") return self.wcf.send_rich_text(name, account, title, digest, url, thumburl, receiver) @@ -100,4 +114,4 @@ class MessageUtil: # 修改使用 allContacts 的地方,改为使用 contact_manager # 例如: # 原来的代码: nickname = self.allContacts.get(wxid, wxid) - # 修改为: nickname = self.contact_manager.get_nickname(wxid) \ No newline at end of file + # 修改为: nickname = self.contact_manager.get_nickname(wxid) diff --git a/plugins/beautyleg/main.py b/plugins/beautyleg/main.py index 0bdb586..50f7dfd 100644 --- a/plugins/beautyleg/main.py +++ b/plugins/beautyleg/main.py @@ -110,8 +110,8 @@ class BeautyLegPlugin(MessagePluginInterface): # 获取随机图片 random_file_path = self._get_random_file_from_dir(self.image_folder) if not random_file_path: - self.message_util.send_text_msg(f"\n❌未找到美腿图片资源", - (roomid if roomid else sender), sender) + self.message_util.send_text(f"\n❌未找到美腿图片资源", + (roomid if roomid else sender), sender) return True, "未找到图片资源" # 发送图片 diff --git a/plugins/dify/main.py b/plugins/dify/main.py index 2951689..a72c609 100644 --- a/plugins/dify/main.py +++ b/plugins/dify/main.py @@ -134,7 +134,7 @@ class DifyPlugin(MessagePluginInterface): query = re.sub(r"@.*?[\u2005|\s]", "", content).strip() if not query: - self.message_util.send_text_msg("请在@我的同时提供问题内容", roomid, sender) + self.message_util.send_text("请在@我的同时提供问题内容", roomid, sender) return False, "没有提供问题内容" # self.message_util.send_text_msg("⏳AI 正在加油,请稍候… 😊", roomid, sender) @@ -153,15 +153,15 @@ class DifyPlugin(MessagePluginInterface): self.message_util.send_file(response, roomid) else: # 如果是普通文本,使用发送文本方法 - self.message_util.send_text_msg(response, roomid, sender) + self.message_util.send_text(response, roomid, sender) return True, "发送成功" else: - self.message_util.send_text_msg("❌未能获取到回复,请稍后再试", roomid, sender) + self.message_util.send_text("❌未能获取到回复,请稍后再试", roomid, sender) return False, "未获取到回复" except Exception as e: self.LOG.error(f"处理Dify聊天请求出错: {e}") - self.message_util.send_text_msg(f"❌请求出错:{str(e)}", roomid, sender) + self.message_util.send_text(f"❌请求出错:{str(e)}", roomid, sender) return False, f"处理出错: {e}" # 原有的命令处理逻辑 @@ -170,8 +170,8 @@ class DifyPlugin(MessagePluginInterface): # 检查命令格式 if len(parts) < 2 or not parts[1].strip(): - self.message_util.send_text_msg(f"{self.command_format}", - (roomid if roomid else sender), sender) + self.message_util.send_text(f"{self.command_format}", + (roomid if roomid else sender), sender) return False, "命令格式错误" # 检查权限 @@ -201,17 +201,17 @@ class DifyPlugin(MessagePluginInterface): self.message_util.send_file(response, (roomid if roomid else sender)) else: # 如果是普通文本,使用发送文本方法 - self.message_util.send_text_msg(response, (roomid if roomid else sender), sender if roomid else "") + self.message_util.send_text(response, (roomid if roomid else sender), sender if roomid else "") return True, "发送成功" else: - self.message_util.send_text_msg("❌未能获取到回复,请稍后再试", (roomid if roomid else sender), - sender if roomid else "") + self.message_util.send_text("❌未能获取到回复,请稍后再试", (roomid if roomid else sender), + sender if roomid else "") return False, "未获取到回复" except Exception as e: self.LOG.error(f"处理Dify聊天请求出错: {e}") - self.message_util.send_text_msg(f"❌请求出错:{str(e)}", (roomid if roomid else sender), - sender if roomid else "") + self.message_util.send_text(f"❌请求出错:{str(e)}", (roomid if roomid else sender), + sender if roomid else "") return False, f"处理出错: {e}" def _chat_with_dify(self, session_id: str, user_id: str, query: str) -> Optional[str]: diff --git a/plugins/game_task/main.py b/plugins/game_task/main.py index 7986e3a..2193f2b 100644 --- a/plugins/game_task/main.py +++ b/plugins/game_task/main.py @@ -148,8 +148,8 @@ class GameTaskPlugin(MessagePluginInterface): 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) + self.message_util.send_text(f"❌未知命令!\n{self.command_format}", + (roomid if roomid else sender), sender) return False, "未知命令" except Exception as e: @@ -162,7 +162,7 @@ class GameTaskPlugin(MessagePluginInterface): # 检查并添加群聊 if not self.encyclopedia_db.check_group_exists(roomid): self.encyclopedia_db.add_group(roomid) - self.message_util.send_text_msg( + self.message_util.send_text( f"🎉 群 {roomid} 已就位,准备开燥!", (roomid if roomid else sender), sender @@ -172,7 +172,7 @@ class GameTaskPlugin(MessagePluginInterface): 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( + self.message_util.send_text( f"🎉 哇塞,{wx_nick_name} 你来啦!\n" f"🌟 群 {roomid} 瞬间燃爆!\n" f"🎈 快来接任务,秀翻全场!指令: /t", @@ -181,7 +181,7 @@ class GameTaskPlugin(MessagePluginInterface): ) except Exception as e: self.LOG.error(f"加入游戏出错: {e}") - self.message_util.send_text_msg( + self.message_util.send_text( f"😔 加入游戏出错,请稍后再试!", (roomid if roomid else sender), sender @@ -198,7 +198,7 @@ class GameTaskPlugin(MessagePluginInterface): # 检查并添加群聊 if not self.encyclopedia_db.check_group_exists(roomid): self.encyclopedia_db.add_group(roomid) - self.message_util.send_text_msg( + self.message_util.send_text( f"🎉 群 {roomid} 已就位,准备开燥!", (roomid if roomid else sender), sender @@ -210,7 +210,7 @@ class GameTaskPlugin(MessagePluginInterface): # 添加当前用户为玩家 self.encyclopedia_db.add_player(sender, roomid, wx_nick_name) - self.message_util.send_text_msg( + self.message_util.send_text( f"🎉 哇塞,{wx_nick_name} 你是第一个玩家!\n" f"🌟 已自动为你加入游戏!\n" f"🎈 现在就为你准备题目...", @@ -223,7 +223,7 @@ class GameTaskPlugin(MessagePluginInterface): player_dict = {p['player_id']: p['player_name'] for p in players} if sender not in player_dict: - self.message_util.send_text_msg( + self.message_util.send_text( f"😅 嘿,你谁啊?\n" f"🌟 先用 /s 报名,不然没法玩哦!", (roomid if roomid else sender), @@ -245,14 +245,14 @@ class GameTaskPlugin(MessagePluginInterface): ) if not active_task_id: - self.message_util.send_text_msg( + self.message_util.send_text( f"😔 任务创建失败,请稍后再试!", (roomid if roomid else sender), sender ) return - self.message_util.send_text_msg( + self.message_util.send_text( f"🎁 {player_dict[sender]},你的专属任务闪亮登场!\n" f"🎀 任务ID: {active_task_id}\n" f"🎈 问题:[{category}]{question}\n" @@ -263,7 +263,7 @@ class GameTaskPlugin(MessagePluginInterface): ) except Exception as e: self.LOG.error(f"获取任务出错: {e}") - self.message_util.send_text_msg( + self.message_util.send_text( f"😔 获取任务出错,请稍后再试!", (roomid if roomid else sender), sender @@ -278,7 +278,7 @@ class GameTaskPlugin(MessagePluginInterface): roomid = message.get("roomid", "") parts = content.split(" ", 2) if len(parts) < 3: - self.message_util.send_text_msg( + self.message_util.send_text( f"😅 喂,格式不对啊!\n" f"🌟 正确姿势:/a [任务ID] [答案]\n" f"🎈 比如:/a 1 钒", @@ -293,7 +293,7 @@ class GameTaskPlugin(MessagePluginInterface): # 获取玩家信息 player = self.encyclopedia_db.get_player(sender, roomid) if not player: - self.message_util.send_text_msg( + self.message_util.send_text( f"😅 嘿,你是路人甲吗?\n" f"🌟 用 /s 先加入群 {roomid} 吧!", (roomid if roomid else sender), @@ -304,7 +304,7 @@ class GameTaskPlugin(MessagePluginInterface): player_name = player['player_name'] if not task_id.isdigit(): - self.message_util.send_text_msg( + self.message_util.send_text( f"😅 喂,任务ID得是数字好吗?\n" f"🌟 比如:1\n" f"🎈 别瞎搞,重新来!", @@ -319,7 +319,7 @@ class GameTaskPlugin(MessagePluginInterface): task_data = self.encyclopedia_db.get_task_by_id(roomid, active_task_id) if not task_data: - self.message_util.send_text_msg( + self.message_util.send_text( f"😔 哎哟,任务 task_{active_task_id} 不翼而飞啦!\n" f"🌼 可能被别人抢先一步咯!", (roomid if roomid else sender), @@ -328,7 +328,7 @@ class GameTaskPlugin(MessagePluginInterface): return False, "0" if task_data['status'] == 'completed': - self.message_util.send_text_msg( + self.message_util.send_text( f"😄 哈哈,你慢了一步!\n" f"🌟 任务 task_{active_task_id} 已经完结\n" f"🎈 快去抢新任务吧!", @@ -362,7 +362,7 @@ class GameTaskPlugin(MessagePluginInterface): self.encyclopedia_db.complete_task(active_task_id) if sender == holder_id: - self.message_util.send_text_msg( + self.message_util.send_text( f"🎉 {player_name} 你是天才吗?\n" f"🌟 任务:{question}\n" f"🎈 答对啦,简直无敌!\n" @@ -372,7 +372,7 @@ class GameTaskPlugin(MessagePluginInterface): sender ) else: - self.message_util.send_text_msg( + self.message_util.send_text( f"🎉 {player_name} 抢答王上线!\n" f"🌟 任务:{question}\n" f"🎈 原主:{holder_name} 被你截胡啦!\n" @@ -385,7 +385,7 @@ class GameTaskPlugin(MessagePluginInterface): # 扣除积分 self.encyclopedia_db.update_player_points(sender, roomid, -1) points = -1 - self.message_util.send_text_msg( + self.message_util.send_text( f"😅 {player_name} 你这是要笑死我吗?\n" f"🌼 任务:{question}\n" f"🎈 你答:{answer}\n" @@ -409,7 +409,7 @@ class GameTaskPlugin(MessagePluginInterface): ranks = self.encyclopedia_db.get_player_ranking(roomid, 10) if not ranks: - self.message_util.send_text_msg( + self.message_util.send_text( f"😔 群 {roomid} 冷冷清清\n" f"🌟 快来一起燥起来吧!", (roomid if roomid else sender), @@ -421,14 +421,14 @@ class GameTaskPlugin(MessagePluginInterface): for i, row in enumerate(ranks, 1): rank_text += f"🐓 {i}. {row['player_name']}: {row['points']} 分\n" - self.message_util.send_text_msg( + self.message_util.send_text( rank_text, (roomid if roomid else sender), sender ) except Exception as e: self.LOG.error(f"显示排行榜出错: {e}") - self.message_util.send_text_msg( + self.message_util.send_text( f"😔 获取排行榜出错,请稍后再试!", (roomid if roomid else sender), sender @@ -441,7 +441,7 @@ class GameTaskPlugin(MessagePluginInterface): tasks = self.encyclopedia_db.get_active_tasks_in_group(roomid) if not tasks: - self.message_util.send_text_msg( + self.message_util.send_text( f"😄 群 {roomid} 现在一片祥和\n" f"🌟 没任务?快用 /t 搞一个!", (roomid if roomid else sender), @@ -457,14 +457,14 @@ class GameTaskPlugin(MessagePluginInterface): f"🌼 大佬:{task['player_name']}\n" ) - self.message_util.send_text_msg( + self.message_util.send_text( task_text, (roomid if roomid else sender), sender ) except Exception as e: self.LOG.error(f"显示活跃任务出错: {e}") - self.message_util.send_text_msg( + self.message_util.send_text( f"😔 获取活跃任务出错,请稍后再试!", (roomid if roomid else sender), sender @@ -477,7 +477,7 @@ class GameTaskPlugin(MessagePluginInterface): tasks = self.encyclopedia_db.get_active_tasks_in_group(roomid) if not tasks: - self.message_util.send_text_msg( + self.message_util.send_text( f"😄 群 {roomid} 全员开挂?\n" f"🌟 没未完成任务,快用 /t 再战!", (roomid if roomid else sender), @@ -493,14 +493,14 @@ class GameTaskPlugin(MessagePluginInterface): f"🌼 主人:{task['player_name']}\n" ) - self.message_util.send_text_msg( + self.message_util.send_text( task_text, (roomid if roomid else sender), sender ) except Exception as e: self.LOG.error(f"列举未完成任务出错: {e}") - self.message_util.send_text_msg( + self.message_util.send_text( f"😔 获取未完成任务出错,请稍后再试!", (roomid if roomid else sender), sender @@ -542,7 +542,7 @@ class GameTaskPlugin(MessagePluginInterface): ) if active_task_id: - self.message_util.send_text_msg( + self.message_util.send_text( f"🎁 新任务来袭,够不够刺激?\n" f"🎀 任务ID: {active_task_id}\n" f"🌟 幸运鹅:{holder_name}\n" diff --git a/plugins/group_member_change/main.py b/plugins/group_member_change/main.py index 3c8799e..df1e03c 100644 --- a/plugins/group_member_change/main.py +++ b/plugins/group_member_change/main.py @@ -201,7 +201,7 @@ class GroupMemberChangePlugin(MessagePluginInterface): 退群时间: {now_time} """ # 使用message_util发送消息 - self.message_util.send_text_msg(message, group_id) + self.message_util.send_text(message, group_id) self.LOG.info(f"已发送退群通知: {nickname} 退出群 {group_id}") def _send_join_notification(self, group_id: str, wxid: str, nickname: str): diff --git a/plugins/group_virtual/README.md b/plugins/group_virtual/README.md new file mode 100644 index 0000000..f081f00 --- /dev/null +++ b/plugins/group_virtual/README.md @@ -0,0 +1,141 @@ +# 跨群聊天插件需求分析与文档 + +## 1. 需求概述 + +开发一个名为"跨群聊天"的微信机器人插件,该插件允许用户将多个微信群组合成一个虚拟聊天组,实现群组间消息的自动转发,使不同群的成员能够进行跨群交流。 + +## 2. 功能需求 + +### 2.1 核心功能 + +1. **群组管理** + - 创建虚拟聊天组 + - 向虚拟聊天组中添加/删除微信群 + - 查看当前所有虚拟聊天组及其包含的群 + - 解散虚拟聊天组 + +2. **消息转发** + - 自动捕获虚拟聊天组内任一群的消息 + - 将消息转发至同一虚拟聊天组内的其他群 + - 支持文本、图片等消息类型的转发(不支持语音、表情) + +3. **消息标识** + - 转发的消息需标明原始发送者信息(昵称) + - 标明消息来源群组 + - 避免消息循环转发 + +### 2.2 扩展功能 + +1. **权限控制** + - 设置管理员权限,只有管理员可以管理虚拟聊天组 + - 支持黑名单功能,可以屏蔽特定用户的消息不被转发 + +2. **消息过滤** + - 支持关键词过滤,含有特定关键词的消息不转发 + - 支持消息类型过滤,可选择只转发特定类型的消息(仅支持文本、图片) + +3. **统计分析** + - 记录转发消息数量 + - 统计各群组活跃度 + +## 3. 技术架构 + +### 3.1 数据结构 + +1. **虚拟聊天组(ChatGroup)** + - ID: 唯一标识符 + - 名称: 虚拟聊天组名称 + - 群组列表: 包含的微信群ID列表 + - 创建时间 + - 创建者 + +2. **消息记录(Message)** + - 消息ID + - 原始群ID + - 发送者ID + - 消息内容 + - 消息类型 + - 发送时间 + - 转发状态 + +### 3.2 接口设计 + +1. **管理接口** + - 创建虚拟聊天组: `createChatGroup(name, creatorId)` + - 添加群到虚拟聊天组: `addGroupToChatGroup(chatGroupId, wxGroupId)` + - 从虚拟聊天组移除群: `removeGroupFromChatGroup(chatGroupId, wxGroupId)` + - 解散虚拟聊天组: `deleteChatGroup(chatGroupId)` + - 查询虚拟聊天组: `listChatGroups()`, `getChatGroupDetail(chatGroupId)` + +2. **消息处理接口** + - 消息接收: `receiveMessage(wxGroupId, senderId, content, type)` + - 消息转发: `forwardMessage(message, targetGroupIds)` + - 消息过滤: `filterMessage(message, rules)` + +## 4. 数据存储 + +使用JSON文件存储虚拟聊天组配置和相关数据: + +```json +{ + "chatGroups": [ + { + "id": "cg001", + "name": "技术交流联盟", + "groups": ["wxid_group1", "wxid_group2", "wxid_group3"], + "createdAt": "2023-05-01T10:00:00Z", + "createdBy": "admin_user_id" + } + ] +} +``` + +## 5. 用户交互 + +### 5.1 命令格式 + +用户通过在群内发送特定格式的命令来管理虚拟聊天组: + +- 创建虚拟聊天组: `#创建跨群 [名称]` +- 添加群到虚拟聊天组: `#添加群 [虚拟聊天组ID] [当前群名称]` +- 查看所有虚拟聊天组: `#查看跨群列表` +- 查看虚拟聊天组详情: `#查看跨群 [虚拟聊天组ID]` +- 退出虚拟聊天组: `#退出跨群 [虚拟聊天组ID]` +- 解散虚拟聊天组: `#解散跨群 [虚拟聊天组ID]` + +### 5.2 消息展示格式 + +转发的消息格式示例: +``` +[来自群"技术交流群"] +张三:这是一条测试消息 +``` + +## 6. 实现步骤 + +1. 创建插件基础结构 +2. 实现数据存储模块 +3. 实现群组管理功能 +4. 实现消息捕获与转发功能 +5. 实现用户命令解析 +6. 添加消息过滤功能 +7. 实现权限控制 +8. 测试与优化 + +## 7. 注意事项 + +1. 避免消息循环转发 +2. 确保消息转发的实时性 +3. 注意微信API的限制,避免触发封号风险 +4. 保护用户隐私,不存储敏感信息 +5. 考虑高并发情况下的性能问题 + +## 8. 后续优化方向 + +1. 支持更多消息类型的转发 +2. 提供Web管理界面 +3. 增加更细粒度的权限控制 +4. 支持定时消息和定向转发 +5. 添加AI智能过滤功能 + +这份需求文档提供了跨群聊天插件的基本框架和实现思路,可以根据实际开发过程进行调整和完善。 \ No newline at end of file diff --git a/plugins/group_virtual/__init__.py b/plugins/group_virtual/__init__.py new file mode 100644 index 0000000..fe0888f --- /dev/null +++ b/plugins/group_virtual/__init__.py @@ -0,0 +1,7 @@ +# 从当前包的main模块导入GroupVirtualPlugin类 +from .main import GroupVirtualPlugin + +# 提供get_plugin函数,返回插件实例 +def get_plugin(): + """获取插件实例""" + return GroupVirtualPlugin() \ No newline at end of file diff --git a/plugins/group_virtual/config.toml b/plugins/group_virtual/config.toml new file mode 100644 index 0000000..7ad18b4 --- /dev/null +++ b/plugins/group_virtual/config.toml @@ -0,0 +1,3 @@ +enable = true +command = ["#创建跨群", "#添加群", "#查看跨群列表", "#查看跨群", "#退出跨群", "#解散跨群"] + diff --git a/plugins/group_virtual/main.py b/plugins/group_virtual/main.py new file mode 100644 index 0000000..afc3ff9 --- /dev/null +++ b/plugins/group_virtual/main.py @@ -0,0 +1,184 @@ +import logging +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.wechat.contact_manager import ContactManager +from db.connection import DBConnectionManager +from db.group_virtual_redis import GroupVirtualRedisDB + + +class GroupVirtualPlugin(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 None # 不使用命令前缀,因为不处理命令 + + @property + def commands(self) -> List[str]: + return [] # 不处理任何命令 + + def __init__(self): + super().__init__() + self.data = None + self.message_cache = set() # 用于防止消息循环转发 + self.group_virtual_redis = None + + def initialize(self, context: Dict[str, Any]) -> bool: + """初始化插件""" + self.LOG = logging.getLogger(f"Plugin.{self.name}") + self.LOG.info(f"正在初始化 {self.name} 插件...") + + # 保存上下文对象 + self.wcf: Wcf = context.get("wcf") + self.event_system = context.get("event_system") + self.message_util: MessageUtil = context.get("message_util") + + # 初始化配置 + self.enable = self._config.get("GroupVirtual", {}).get("enable", True) + + # 初始化Redis数据库操作类 + self.db_manager = DBConnectionManager.get_instance() + self.group_virtual_redis = GroupVirtualRedisDB(self.db_manager) + + # 从Redis加载数据 + self.data = self.group_virtual_redis.load_chat_groups() + + self.LOG.info(f"[{self.name}] 插件初始化完成") + 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 + + # 只处理群消息 + roomid = message.get("roomid", "") + if not roomid: + return False + + # 检查该群是否在任何虚拟聊天组中 + chat_groups = self._get_chat_groups_by_group_id(roomid) + return len(chat_groups) > 0 + + @plugin_stats_decorator(plugin_name="跨群聊天") + def process_message(self, message: Dict[str, Any]) -> Tuple[bool, Optional[str]]: + """处理消息""" + roomid = message.get("roomid", "") + sender = message.get("sender", "") + + # 检查是否是机器人自己发送的消息 + if sender == self.wcf.self_wxid: + return False, "不转发自己的消息" + + # 获取该群所在的所有虚拟聊天组 + chat_groups = self._get_chat_groups_by_group_id(roomid) + if not chat_groups: + return False, "该群不在任何虚拟聊天组中" + + # 生成消息唯一标识(用于防止循环转发) + message_id = f"{message.get('id')}_{message.get('timestamp')}" + if message_id in self.message_cache: + return False, "消息已经转发过" + + # 添加到缓存 + self.message_cache.add(message_id) + # 缓存大小控制 + if len(self.message_cache) > 1000: + self.message_cache = set(list(self.message_cache)[-500:]) + + # 处理每个虚拟聊天组的转发 + for chat_group in chat_groups: + self._forward_message_in_chat_group(message, chat_group) + + return True, "消息已转发" + + def _forward_message_in_chat_group(self, message: Dict[str, Any], chat_group: Dict[str, Any]): + """在虚拟聊天组内转发消息""" + roomid = message.get("roomid", "") + sender = message.get("sender", "") + con = ContactManager.get_instance() + # 获取发送者昵称 + sender_name = con.get_contact_name(sender) or "未知用户" + + # 获取源群名称 + source_group_name = None + for group in chat_group["groups"]: + if group["id"] == roomid: + source_group_name = group["name"] + break + + if not source_group_name: + source_group_name = "未知群组" + + # 构建转发消息前缀 + prefix = f"[{sender_name}@{source_group_name}]:" + + # 根据消息类型处理 + msg_type = message.get("type", 0) + + # 文本消息 + if msg_type == 1: + content = message.get("content", "") + forward_content = f"{prefix}{content}" + + # 转发到其他群 + for group in chat_group["groups"]: + if group["id"] != roomid: # 不转发回源群 + self.message_util.send_text(forward_content, group["id"]) + + # 图片消息 暂时不支持。 + # elif msg_type == 3: + # # 先发送前缀 + # for group in chat_group["groups"]: + # if group["id"] != roomid: + # self.message_util.send_text(f"{prefix}[图片]", group["id"]) + # # 转发图片 + # image_path = message.get("file_path", "") + # if image_path and os.path.exists(image_path): + # self.message_util.send_image(image_path, group["id"]) + + def _get_chat_group(self, chat_group_id: str) -> Optional[Dict[str, Any]]: + """获取虚拟聊天组""" + return self.group_virtual_redis.get_chat_group(chat_group_id) + + def _get_chat_groups_by_group_id(self, group_id: str) -> List[Dict[str, Any]]: + """获取包含指定群的所有虚拟聊天组""" + return self.group_virtual_redis.get_chat_groups_by_group_id(group_id) + + def _save_data(self): + """保存数据到Redis""" + self.group_virtual_redis.save_chat_groups(self.data) \ No newline at end of file diff --git a/plugins/message_sign/main.py b/plugins/message_sign/main.py index ab957a0..01b818c 100644 --- a/plugins/message_sign/main.py +++ b/plugins/message_sign/main.py @@ -240,8 +240,8 @@ class MessageSignPlugin(MessagePluginInterface): # 如果 sign_stat 已经大于或等于今天的零点,则认为用户已经签到过了 if sign_stat >= today_start: - self.message_util.send_text_msg(f"您今天已经签到过了!", - (roomid if roomid else sender), sender) + self.message_util.send_text(f"您今天已经签到过了!", + (roomid if roomid else sender), sender) return False, "已签到" # 在_handle_sign_in方法中,修改断签处理逻辑 @@ -316,13 +316,13 @@ class MessageSignPlugin(MessagePluginInterface): daily_vocab = self.get_random_vocabulary() output += f"\n今日词汇:{daily_vocab}" - self.message_util.send_text_msg(output, (roomid if roomid else sender), sender) + self.message_util.send_text(output, (roomid if roomid else sender), sender) return True, "签到成功" except Exception as e: self.LOG.error(f"处理签到请求出错: {e}") - self.message_util.send_text_msg(f"签到出错:{e}", - (roomid if roomid else sender), sender) + self.message_util.send_text(f"签到出错:{e}", + (roomid if roomid else sender), sender) return False, f"处理出错: {e}" def reset_today_count_if_needed(self): @@ -373,7 +373,7 @@ class MessageSignPlugin(MessagePluginInterface): # 检查用户是否有签到记录 if not user_record: - self.message_util.send_text_msg( + self.message_util.send_text( "❌ 您还没有签到记录,请先进行签到!", (roomid if roomid else sender), sender ) @@ -406,7 +406,7 @@ class MessageSignPlugin(MessagePluginInterface): else: # 昨天已经签到了或者断签超过一天,不需要补签 self.LOG.info(f"不符合补签条件,last_sign_date: {last_sign_date}, sign_stat: {sign_stat}") - self.message_util.send_text_msg( + self.message_util.send_text( "❌ 您昨天已经签到过了或断签超过一天,不符合补签条件!", (roomid if roomid else sender), sender ) @@ -415,7 +415,7 @@ class MessageSignPlugin(MessagePluginInterface): # 今天未签到,检查是否符合补签条件(只能补签昨天) if not last_sign_date or last_sign_date < day_before_yesterday: self.LOG.info(f"断签超过一天,last_sign_date: {last_sign_date}") - self.message_util.send_text_msg( + self.message_util.send_text( "❌ 只能补签断签一天的情况!您已断签超过一天或没有签到记录。", (roomid if roomid else sender), sender ) @@ -423,7 +423,7 @@ class MessageSignPlugin(MessagePluginInterface): if last_sign_date >= yesterday: self.LOG.info(f"昨天已签到,last_sign_date: {last_sign_date}") - self.message_util.send_text_msg( + self.message_util.send_text( "❌ 您昨天已经签到过了,不需要补签!", (roomid if roomid else sender), sender ) @@ -435,7 +435,7 @@ class MessageSignPlugin(MessagePluginInterface): user_points = points_db.get_user_points(sender, roomid) if not user_points or user_points["total_points"] < self.makeup_cost: - self.message_util.send_text_msg( + self.message_util.send_text( f"❌ 积分不足!补签需要 {self.makeup_cost} 积分,您当前只有 {user_points.get('total_points', 0)} 积分。", (roomid if roomid else sender), sender ) @@ -448,7 +448,7 @@ class MessageSignPlugin(MessagePluginInterface): ) if not deduct_success: - self.message_util.send_text_msg( + self.message_util.send_text( f"❌ 扣除积分失败:{deduct_result.get('error', '未知错误')}", (roomid if roomid else sender), sender ) @@ -507,7 +507,7 @@ class MessageSignPlugin(MessagePluginInterface): success_message += f"💰 当前积分:{user_points['total_points'] - self.makeup_cost}" - self.message_util.send_text_msg( + self.message_util.send_text( success_message, (roomid if roomid else sender), sender ) @@ -516,7 +516,7 @@ class MessageSignPlugin(MessagePluginInterface): except Exception as e: self.LOG.error(f"处理补签请求出错: {e}") - self.message_util.send_text_msg( + self.message_util.send_text( f"❌ 补签出错:{e}", (roomid if roomid else sender), sender ) diff --git a/plugins/point_trade/main.py b/plugins/point_trade/main.py index 4422acb..ae566ad 100644 --- a/plugins/point_trade/main.py +++ b/plugins/point_trade/main.py @@ -149,8 +149,8 @@ class PointTradePlugin(MessagePluginInterface): elif command[0] in self._commands: return self._handle_transfer_points(message) else: - self.message_util.send_text_msg(f"❌未知命令!{self.command_format}", - (roomid if roomid else sender), sender) + self.message_util.send_text(f"❌未知命令!{self.command_format}", + (roomid if roomid else sender), sender) return True, "未知命令" def _handle_transfer_points(self, message: Dict[str, Any]) -> Tuple[bool, Optional[str]]: @@ -164,21 +164,21 @@ class PointTradePlugin(MessagePluginInterface): # 检查命令格式 if len(command) < 3: - self.message_util.send_text_msg(f"❌命令格式错误!积分转账 积分数 @用户", - (roomid if roomid else sender), sender) + self.message_util.send_text(f"❌命令格式错误!积分转账 积分数 @用户", + (roomid if roomid else sender), sender) return True, "命令格式错误" # 检查积分数是否为正整数 if not command[1].isdigit(): - self.message_util.send_text_msg(f"🈚️转账积分无效(必须为正整数!) \n积分转账 积分数 @用户", - (roomid if roomid else sender), sender) + self.message_util.send_text(f"🈚️转账积分无效(必须为正整数!) \n积分转账 积分数 @用户", + (roomid if roomid else sender), sender) return True, "积分无效" # 检查@用户是否有效 at_users = self.at_list(xml) if len(at_users) != 1: - self.message_util.send_text_msg(f"转账失败❌\n🈚️转账人无效! \n积分转账 积分数 @用户", - (roomid if roomid else sender), sender) + self.message_util.send_text(f"转账失败❌\n🈚️转账人无效! \n积分转账 积分数 @用户", + (roomid if roomid else sender), sender) return True, "转账人无效" reward_points = int(command[1]) @@ -197,11 +197,11 @@ class PointTradePlugin(MessagePluginInterface): error_msg = result.get("error", "未知错误") if "积分不足" in error_msg: current_points = result.get("current_points", 0) - self.message_util.send_text_msg( + self.message_util.send_text( f"❌转账失败!\n你的积分不足以进行转账!当前积分:{current_points},你需要 {reward_points} 积分。", (roomid if roomid else sender), sender) else: - self.message_util.send_text_msg( + self.message_util.send_text( f"❌转账失败!\n{error_msg}", (roomid if roomid else sender), sender) return True, f"转账失败: {error_msg}" @@ -224,13 +224,13 @@ class PointTradePlugin(MessagePluginInterface): f"👤{to_user_name} 当前积分: {to_user.get('total_points', 0)}" ) - self.message_util.send_text_msg(output, (roomid if roomid else sender), sender) + self.message_util.send_text(output, (roomid if roomid else sender), sender) return True, "转账成功" except Exception as e: self.LOG.error(f"积分交易出错: {e}") - self.message_util.send_text_msg(f"❌积分交易失败!请稍后重试。错误: {str(e)}", - (roomid if roomid else sender), sender) + self.message_util.send_text(f"❌积分交易失败!请稍后重试。错误: {str(e)}", + (roomid if roomid else sender), sender) return True, f"处理出错: {str(e)}" def _handle_my_points(self, message: Dict[str, Any]) -> Tuple[bool, Optional[str]]: @@ -243,8 +243,8 @@ class PointTradePlugin(MessagePluginInterface): user_points = self.points_db.get_user_points(sender, roomid) if not user_points: - self.message_util.send_text_msg(f"❌未找到你的积分记录!请先参与积分活动[签到,答题/t]。", - (roomid if roomid else sender), sender) + self.message_util.send_text(f"❌未找到你的积分记录!请先参与积分活动[签到,答题/t]。", + (roomid if roomid else sender), sender) return False, "未找到积分记录" # 获取用户昵称 @@ -299,13 +299,13 @@ class PointTradePlugin(MessagePluginInterface): f"{recent_txs}" ) - self.message_util.send_text_msg(output, (roomid if roomid else sender), sender) + self.message_util.send_text(output, (roomid if roomid else sender), sender) return True, "查询积分成功" except Exception as e: self.LOG.error(f"查询积分出错: {e}") - self.message_util.send_text_msg(f"❌查询积分失败!请稍后重试。错误: {str(e)}", - (roomid if roomid else sender), sender) + self.message_util.send_text(f"❌查询积分失败!请稍后重试。错误: {str(e)}", + (roomid if roomid else sender), sender) return True, f"处理出错: {str(e)}" def _handle_points_ranking(self, message: Dict[str, Any]) -> Tuple[bool, Optional[str]]: @@ -315,7 +315,7 @@ class PointTradePlugin(MessagePluginInterface): wcf: Wcf = message.get("wcf") if not roomid: - self.message_util.send_text_msg("❌积分排行榜仅在群聊中可用!", sender, "") + self.message_util.send_text("❌积分排行榜仅在群聊中可用!", sender, "") return True, "非群聊环境" try: @@ -323,7 +323,7 @@ class PointTradePlugin(MessagePluginInterface): ranking = self.points_db.get_points_ranking(roomid, 10) if not ranking: - self.message_util.send_text_msg("❌暂无积分排行数据!请先参与积分活动。", roomid, sender) + self.message_util.send_text("❌暂无积分排行数据!请先参与积分活动。", roomid, sender) return True, "无排行数据" # 构建排行榜消息 @@ -348,12 +348,12 @@ class PointTradePlugin(MessagePluginInterface): f"更新时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}" ) - self.message_util.send_text_msg(output, roomid, sender) + self.message_util.send_text(output, roomid, sender) return True, "查询排行榜成功" except Exception as e: self.LOG.error(f"查询积分排行榜出错: {e}") - self.message_util.send_text_msg(f"❌查询积分排行榜失败!请稍后重试。错误: {str(e)}", roomid, sender) + self.message_util.send_text(f"❌查询积分排行榜失败!请稍后重试。错误: {str(e)}", roomid, sender) return True, f"处理出错: {str(e)}" def at_list(self, xml): @@ -457,13 +457,13 @@ class PointTradePlugin(MessagePluginInterface): # 检查是否在群聊中 if not roomid: - self.message_util.send_text_msg("❌打劫功能仅在群聊中可用!", sender, "") + self.message_util.send_text("❌打劫功能仅在群聊中可用!", sender, "") return True, "非群聊环境" # 检查时间限制 - 只允许在18:00-21:00之间打劫 current_hour = datetime.now().hour if current_hour < 18 or current_hour >= 21: - self.message_util.send_text_msg("❌打劫功能仅在晚上18:00-21:00之间开放!请在开放时间再来。", roomid, sender) + self.message_util.send_text("❌打劫功能仅在晚上18:00-21:00之间开放!请在开放时间再来。", roomid, sender) return True, "时间限制" # 检查是否在押 @@ -472,8 +472,8 @@ class PointTradePlugin(MessagePluginInterface): remaining_time = prison_status['end_time'] - datetime.now() hours = int(remaining_time.total_seconds() / 3600) minutes = int((remaining_time.total_seconds() % 3600) / 60) - self.message_util.send_text_msg(f"❌你正在服刑!\n剩余时间: {hours}小时{minutes}分钟\n可请求他人花费30积分保释。", roomid, - sender) + self.message_util.send_text(f"❌你正在服刑!\n剩余时间: {hours}小时{minutes}分钟\n可请求他人花费30积分保释。", roomid, + sender) return True, "在押状态" # 检查冷却时间 @@ -484,15 +484,15 @@ class PointTradePlugin(MessagePluginInterface): if time_passed < self.rob_cooldown: remaining_time = int(self.rob_cooldown - time_passed) minutes, seconds = divmod(remaining_time, 60) - self.message_util.send_text_msg(f"❌你最近已经打劫过了,需要冷却 {minutes}分{seconds}秒 后才能再次打劫!", - roomid, sender) + self.message_util.send_text(f"❌你最近已经打劫过了,需要冷却 {minutes}分{seconds}秒 后才能再次打劫!", + roomid, sender) return True, "冷却中" # 检查@用户是否有效 at_users = self.at_list(xml) if len(at_users) != 1: - self.message_util.send_text_msg(f"打劫失败❌\n请指定一个打劫目标!\n打劫 @用户", - roomid, sender) + self.message_util.send_text(f"打劫失败❌\n请指定一个打劫目标!\n打劫 @用户", + roomid, sender) return True, "目标无效" target_wxid = next(iter(at_users)) @@ -500,7 +500,7 @@ class PointTradePlugin(MessagePluginInterface): # 不能打劫自己 if target_wxid == robber_wxid: - self.message_util.send_text_msg("❌你不能打劫自己!", roomid, sender) + self.message_util.send_text("❌你不能打劫自己!", roomid, sender) return True, "不能打劫自己" try: @@ -509,11 +509,11 @@ class PointTradePlugin(MessagePluginInterface): target_points = self.points_db.get_user_points(target_wxid, roomid) if not robber_points: - self.message_util.send_text_msg("❌你没有积分记录,无法进行打劫!请先参与积分活动。", roomid, sender) + self.message_util.send_text("❌你没有积分记录,无法进行打劫!请先参与积分活动。", roomid, sender) return True, "打劫者无积分" if not target_points: - self.message_util.send_text_msg("❌目标没有积分记录,无法进行打劫!", roomid, sender) + self.message_util.send_text("❌目标没有积分记录,无法进行打劫!", roomid, sender) return True, "目标无积分" robber_total = robber_points.get('total_points', 0) @@ -521,11 +521,11 @@ class PointTradePlugin(MessagePluginInterface): # 检查最低积分要求 if robber_total < self.rob_min_points: - self.message_util.send_text_msg(f"❌你的积分不足 {self.rob_min_points} 点,无法进行打劫!", roomid, sender) + self.message_util.send_text(f"❌你的积分不足 {self.rob_min_points} 点,无法进行打劫!", roomid, sender) return True, "打劫者积分不足" if target_total < self.rob_min_points: - self.message_util.send_text_msg(f"❌目标积分不足 {self.rob_min_points} 点,不值得打劫!", roomid, sender) + self.message_util.send_text(f"❌目标积分不足 {self.rob_min_points} 点,不值得打劫!", roomid, sender) return True, "目标积分不足" # 获取用户昵称 @@ -581,10 +581,10 @@ class PointTradePlugin(MessagePluginInterface): f"👤{target_name} 当前积分: {from_user.get('total_points', 0)}" ) - self.message_util.send_text_msg(output, roomid, sender) + self.message_util.send_text(output, roomid, sender) return True, "打劫成功" else: - self.message_util.send_text_msg(f"❌打劫过程中出现问题:{result.get('error', '未知错误')}", roomid, sender) + self.message_util.send_text(f"❌打劫过程中出现问题:{result.get('error', '未知错误')}", roomid, sender) return True, "打劫失败" else: @@ -618,15 +618,15 @@ class PointTradePlugin(MessagePluginInterface): f"👤{robber_name} 当前积分: {from_user.get('total_points', 0)}" ) - self.message_util.send_text_msg(output, roomid, sender) + self.message_util.send_text(output, roomid, sender) return True, "打劫失败" else: - self.message_util.send_text_msg(f"❌处理打劫惩罚时出现问题:{result.get('error', '未知错误')}", roomid, sender) + self.message_util.send_text(f"❌处理打劫惩罚时出现问题:{result.get('error', '未知错误')}", roomid, sender) return True, "处理惩罚失败" except Exception as e: self.LOG.error(f"处理打劫请求出错: {e}") - self.message_util.send_text_msg(f"❌打劫过程中出现意外:{str(e)}", roomid, sender) + self.message_util.send_text(f"❌打劫过程中出现意外:{str(e)}", roomid, sender) return True, f"处理出错: {str(e)}" def _handle_bailout(self, message: Dict[str, Any]) -> Tuple[bool, Optional[str]]: @@ -638,14 +638,14 @@ class PointTradePlugin(MessagePluginInterface): # 检查是否在群聊中 if not roomid: - self.message_util.send_text_msg("❌保释功能仅在群聊中可用!", sender, "") + self.message_util.send_text("❌保释功能仅在群聊中可用!", sender, "") return True, "非群聊环境" # 检查@用户是否有效 at_users = self.at_list(xml) if len(at_users) != 1: - self.message_util.send_text_msg(f"保释失败❌\n请指定一个保释目标!\n保释 @用户", - roomid, sender) + self.message_util.send_text(f"保释失败❌\n请指定一个保释目标!\n保释 @用户", + roomid, sender) return True, "目标无效" prisoner_wxid = next(iter(at_users)) @@ -653,7 +653,7 @@ class PointTradePlugin(MessagePluginInterface): # 不能保释自己 if prisoner_wxid == bailout_wxid: - self.message_util.send_text_msg("❌你不能保释自己!", roomid, sender) + self.message_util.send_text("❌你不能保释自己!", roomid, sender) return True, "不能保释自己" try: @@ -672,13 +672,13 @@ class PointTradePlugin(MessagePluginInterface): f"✅ 保释成功!\n" f"👤{bailout_name} 花费30积分保释了 👤{prisoner_name}" ) - self.message_util.send_text_msg(output, roomid, sender) + self.message_util.send_text(output, roomid, sender) return True, "保释成功" else: - self.message_util.send_text_msg(f"❌保释失败: {message}", roomid, sender) + self.message_util.send_text(f"❌保释失败: {message}", roomid, sender) return True, "保释失败" except Exception as e: self.LOG.error(f"处理保释请求出错: {e}") - self.message_util.send_text_msg(f"❌保释过程中出现意外:{str(e)}", roomid, sender) + self.message_util.send_text(f"❌保释过程中出现意外:{str(e)}", roomid, sender) return True, f"处理出错: {str(e)}" diff --git a/plugins/video_man/main.py b/plugins/video_man/main.py index ced35e9..85752bf 100644 --- a/plugins/video_man/main.py +++ b/plugins/video_man/main.py @@ -109,8 +109,8 @@ class VideoManPlugin(MessagePluginInterface): file_abspath = self._download_video("https://api.guiguiya.com/api/video/fuji?type=json") if not file_abspath: - self.message_util.send_text_msg(f"\n❌视频下载失败,请稍后再试", - (roomid if roomid else sender), sender) + self.message_util.send_text(f"\n❌视频下载失败,请稍后再试", + (roomid if roomid else sender), sender) return False, "视频下载失败" # 发送视频 diff --git a/robot.py b/robot.py index ab1ac4a..9b6b3ae 100644 --- a/robot.py +++ b/robot.py @@ -171,7 +171,7 @@ class Robot(Job): if msg.from_group() and self.gbm.get_group_permission(msg.roomid, Feature.AI_CAPABILITY) == PermissionStatus.ENABLED: resp = self.gbm.get_enabled_features(msg.roomid) - self.message_util.send_text_msg(resp, (msg.roomid if msg.from_group() else msg.sender), msg.sender) + self.message_util.send_text(resp, (msg.roomid if msg.from_group() else msg.sender), msg.sender) return True else: return True