diff --git a/group_auto/robot_command.py b/group_auto/robot_command.py new file mode 100644 index 0000000..01edcd9 --- /dev/null +++ b/group_auto/robot_command.py @@ -0,0 +1,243 @@ +# 群清单管理 +# 群功能管理 +# 0.加入或者关闭群机器人 #启用群机器人 #关闭群机器人 +# 1.每日新闻自动播报 #启用每日新闻播报 #关闭每日新闻播报 +# 2.每日群发言总结 #启用群发言 #关闭群发言 +# 3.群AI能力 #启用群AI #关闭群AI +# 4.群总结能力 #启用群总结 #关闭群总结 +# 5.sehuatang PDF能力 #启用pdf #关闭pdf +import redis +import json +from enum import Enum + +# 连接到本地 Redis 服务 +r = redis.StrictRedis(host='192.168.2.32', port=6379, db=0, decode_responses=True) + + +class PermissionStatus(Enum): + """权限状态枚举""" + ENABLED = "enabled" + DISABLED = "disabled" + + +class Feature(Enum): + """功能权限枚举,带序号""" + ROBOT = 1, "群机器人" + DAILY_NEWS = 2, "每日新闻自动播报" + DAILY_SUMMARY = 3, "每日群发言总结" + AI_CAPABILITY = 4, "群AI能力" + SUMMARY_CAPABILITY = 5, "群总结能力" + PDF_CAPABILITY = 6, "sehuatang PDF能力" + EPIC = 7, "EPIC自动播报" + + def __new__(cls, value, description): + obj = object.__new__(cls) + obj._value_ = value + obj.description = description # 添加描述 + return obj + + def __str__(self): + return self.description + + +class GroupBotManager: + """群机器人管理,支持本地缓存""" + + # 本地缓存 + local_cache = { + "group_permissions": {}, # 用于缓存群组功能权限 + "group_list": set() # 用于缓存 group:list + } + + @staticmethod + def load_local_cache(): + """从 Redis 加载数据到本地缓存""" + group_list = r.smembers("group:list") + GroupBotManager.local_cache["group_list"] = set(group_list) + + # 加载群组权限 + for group_id in GroupBotManager.local_cache["group_list"]: + key = f'group:{group_id}:permissions' + GroupBotManager.local_cache["group_permissions"][group_id] = {} + for feature in Feature: + status_value = r.hget(key, feature.name) + if status_value: + GroupBotManager.local_cache["group_permissions"][group_id][feature] = PermissionStatus(status_value) + else: + GroupBotManager.local_cache["group_permissions"][group_id][feature] = PermissionStatus.DISABLED + + @staticmethod + def save_to_redis(): + """将本地缓存保存回 Redis""" + # 保存 group:list 到 Redis + r.sadd("group:list", *GroupBotManager.local_cache["group_list"]) + + # 保存每个群组的权限到 Redis + for group_id, permissions in GroupBotManager.local_cache["group_permissions"].items(): + key = f'group:{group_id}:permissions' + for feature, status in permissions.items(): + r.hset(key, feature.name, status.value) + + @staticmethod + def set_group_permission(group_id, feature: Feature, status: PermissionStatus): + """设置群组功能权限并更新本地缓存""" + # 更新本地缓存 + if group_id not in GroupBotManager.local_cache["group_permissions"]: + GroupBotManager.local_cache["group_permissions"][group_id] = {} + + GroupBotManager.local_cache["group_permissions"][group_id][feature] = status + + # 同步到 Redis + key = f'group:{group_id}:permissions' + r.hset(key, feature.name, status.value) + + @staticmethod + def get_group_permission(group_id, feature: Feature): + """获取群组某个功能的权限状态""" + # 先从本地缓存获取 + if group_id in GroupBotManager.local_cache["group_permissions"]: + return GroupBotManager.local_cache["group_permissions"][group_id].get(feature, PermissionStatus.DISABLED) + else: + return PermissionStatus.DISABLED + + @staticmethod + def handle_command(group_id, command_str): + """统一处理群功能指令""" + # 命令解析 + command_parts = command_str.strip().split("-") + + # 如果是MENU指令,返回功能列表 + if command_str.strip().upper() == "MENU": + return GroupBotManager.display_menu() + + # 如果是GROUP_LIST指令,返回 group:list 清单 + if command_str.strip().upper() == "GROUP_LIST": + return GroupBotManager.get_group_list() + + if len(command_parts) < 2: + return "无效命令,格式应为:功能-操作 或 序号-操作" + + feature_str = command_parts[0] + action = command_parts[1] + + # 如果第一个参数是序号,则转化为对应的功能 + if feature_str.isdigit(): + feature_num = int(feature_str) + try: + feature = Feature(feature_num) # 使用枚举序号查找功能 + except ValueError: + return "无效的功能序号" + else: + try: + feature = Feature[feature_str] # 通过枚举名称获取功能枚举 + except KeyError: + return "无效功能名称" + + # 处理群机器人的启用和关闭(特别操作:更新 group:list) + if feature == Feature.ROBOT: + if action == "启用": + GroupBotManager.set_group_permission(group_id, feature, PermissionStatus.ENABLED) + # 启用群机器人时,将 group_id 加入 group:list + GroupBotManager.local_cache["group_list"].add(group_id) + # 同步到 Redis + r.sadd("group:list", group_id) + return f"群机器人已启用,群组 {group_id} 已加入 group:list" + elif action == "关闭": + GroupBotManager.set_group_permission(group_id, feature, PermissionStatus.DISABLED) + # 关闭群机器人时,从 group:list 中删除 group_id + GroupBotManager.local_cache["group_list"].remove(group_id) + # 同步到 Redis + r.srem("group:list", group_id) + return f"群机器人已关闭,群组 {group_id} 已从 group:list 移除" + else: + return "无效操作,仅支持启用或关闭" + + # 先检查群机器人权限 + robot_status = GroupBotManager.get_group_permission(group_id, Feature.ROBOT) + if robot_status != PermissionStatus.ENABLED and feature != Feature.ROBOT: + return "群机器人未启用,无法执行其他功能操作" + + # 根据不同的操作启用或禁用功能 + if action == "启用": + GroupBotManager.set_group_permission(group_id, feature, PermissionStatus.ENABLED) + return f"{feature.description} 已启用" + elif action == "关闭": + GroupBotManager.set_group_permission(group_id, feature, PermissionStatus.DISABLED) + return f"{feature.description} 已关闭" + else: + return "无效操作,仅支持启用或关闭" + + @staticmethod + def list_group_permissions(group_id): + """列出群组所有功能及其状态""" + permissions = {} + for feature in Feature: + status = GroupBotManager.get_group_permission(group_id, feature) + permissions[feature] = status if status else PermissionStatus.DISABLED # 默认为禁用状态 + return permissions + + @staticmethod + def display_menu(): + """显示所有功能列表及其当前状态""" + menu = [] + for feature in Feature: + menu.append(f"{feature.value}. {feature.description}") + return "\n".join(menu) + + @staticmethod + def get_group_list(): + """返回所有启用了群机器人的群组清单,格式为集合""" + return list(GroupBotManager.local_cache["group_list"]) + + +# 示例命令 +def simulate_commands(): + # 加载本地缓存 + GroupBotManager.load_local_cache() + + group_id = "12345" + + # 启用群机器人 + print(GroupBotManager.handle_command(group_id, "ROBOT-启用")) + + # 启用每日新闻自动播报 + print(GroupBotManager.handle_command(group_id, "2-启用")) # 使用序号启用每日新闻自动播报 + + # 关闭群AI能力 + print(GroupBotManager.handle_command(group_id, "4-关闭")) # 使用序号关闭群AI能力 + + # 启用群总结能力 + print(GroupBotManager.handle_command(group_id, "5-启用")) # 使用序号启用群总结能力 + + # 关闭Sehuatang PDF能力 + print(GroupBotManager.handle_command(group_id, "6-关闭")) # 使用序号关闭Sehuatang PDF能力 + + # 查看当前群组的功能权限 + print(GroupBotManager.get_group_permission(group_id, Feature.ROBOT)) + print(GroupBotManager.get_group_permission(group_id, Feature.DAILY_NEWS)) + print(GroupBotManager.get_group_permission(group_id, Feature.AI_CAPABILITY)) + print(GroupBotManager.get_group_permission(group_id, Feature.SUMMARY_CAPABILITY)) + print(GroupBotManager.get_group_permission(group_id, Feature.PDF_CAPABILITY)) + + # 查看群组所有功能和状态 + permissions = GroupBotManager.list_group_permissions(group_id) + for feature, status in permissions.items(): + print(f"{feature.description} (序号: {feature.value}): {status.value}") + + # 查看 group:list 中的群组 + print("当前启用群机器人的群组:", GroupBotManager.get_group_list()) + + # 查看菜单功能列表 + print("功能列表:") + print(GroupBotManager.handle_command(group_id, "MENU")) + + # 查看 group:list 清单 + print("群组清单:") + print(GroupBotManager.handle_command(group_id, "GROUP_LIST")) + + # 保存到 Redis + GroupBotManager.save_to_redis() + + +# 执行模拟命令 +simulate_commands() diff --git a/main.py b/main.py index eec57b3..f77046d 100644 --- a/main.py +++ b/main.py @@ -50,22 +50,20 @@ def main(chat_type: int): # robot.onEveryTime("07:00", weather_report, robot=robot) # 每天 8:30 发送新闻 - robot.onEveryTime("08:30", robot.newsBaiduReport) + robot.onEveryTime("08:30", robot.newsBaiduReportAuto) # 每天 16:30 提醒发日报周报月报 # robot.onEveryTime("10:30", ReportReminder.remind, robot=robot) # epic robot.onEveryTime("10:30", robot.sendEpicFreeGames) - # message report 1:数据自动从redis 转到sqllite robot.onEveryTime("00:30", robot.messageCountToDB) # 从db中提取并发送给相关群 robot.onEveryTime("09:30", robot.generateAndSendRanking) - - #sehuatang - robot.onEveryTime("15:00",robot.generateSehuatangPdf) + # sehuatang + robot.onEveryTime("15:00", robot.generateSehuatangPdf) # 让机器人一直跑 robot.keepRunningAndBlockProcess() diff --git a/robot.py b/robot.py index 8f39e2f..b940c44 100644 --- a/robot.py +++ b/robot.py @@ -23,7 +23,9 @@ from base.func_xinghuo_web import XinghuoWeb from base.func_claude import Claude from configuration import Config from constants import ChatType +from group_auto.robot_command import GroupBotManager from job_mgmt import Job +from group_auto.robot_command import Feature __version__ = "39.2.4.0" @@ -44,7 +46,8 @@ class Robot(Job): self.LOG = logging.getLogger("Robot") self.wxid = self.wcf.get_self_wxid() self.allContacts = self.getAllContacts() - + GroupBotManager.load_local_cache() + self.gbm = GroupBotManager() if ChatType.is_in_chat_types(chat_type): if chat_type == ChatType.TIGER_BOT.value and TigerBot.value_check(self.config.TIGERBOT): self.chat = TigerBot(self.config.TIGERBOT) @@ -127,6 +130,12 @@ class Robot(Job): def toChitchat(self, msg: WxMsg) -> bool: """闲聊,接入 ChatGPT """ + # 如果聊天内容来自自己,则进行指令判断 + if msg.from_self() and msg.from_group(): + command_str = re.sub(r"@.*?[\u2005|\s]", "", msg.content).replace(" ", "") + rsp = GroupBotManager.handle_command(msg.roomid, command_str) + self.sendTextMsg(rsp, msg.roomid, msg.sender) + return True if not self.chat: # 没接 ChatGPT,固定回复 rsp = "你@我干嘛?" else: # 接了 ChatGPT,智能回复 @@ -140,6 +149,7 @@ class Robot(Job): elif q == '/总结': self.message_summary_robot((msg.roomid if msg.from_group() else msg.sender)) return True + # 群管理自动加入,减少服务重启管理 else: rsp = self.chat.get_answer(q, (msg.roomid if msg.from_group() else msg.sender)) @@ -301,18 +311,40 @@ class Robot(Job): self.allContacts[msg.sender] = nickName[0] self.sendTextMsg(f"Hi {nickName[0]},我自动通过了你的好友请求。", msg.sender) + def send_group_txt_message(self, msg: str, feature: Feature): + try: + receivers = self.gbm.get_group_list() + if not receivers: + return + for r in receivers: + if self.gbm.get_group_permission(r, feature): + self.sendTextMsg(msg, r) + except Exception as e: + self.LOG.error(f"send_group_txt_message:{feature.description} error:{e}") + + def send_group_file_message(self, path: str, feature: Feature): + try: + receivers = self.gbm.get_group_list() + if not receivers: + return + for r in receivers: + if self.gbm.get_group_permission(r, feature): + self.wcf.send_file(path, r) + except Exception as e: + self.LOG.error(f"send_group_file_message:{feature.description} error:{e}") + + # ============================================== 业务内容========================================================== + + def newsBaiduReportAuto(self) -> None: + try: + news = News().get_baidu_news() + self.send_group_txt_message(news, Feature.DAILY_NEWS) + except Exception as e: + self.LOG.error(f"newsBaiduReportAuto error:{e}") + def newsBaiduReport(self, sender: str = None) -> None: try: news = News().get_baidu_news() - # news = ( - # f"请根据新闻标题,按照新闻的类型(财经、彩票、房产、股票、家居、教育、科技、社会、时尚、时政、体育、星座、游戏、娱乐)进行分类;内容前加入当前日期和星期几" \ - # "内容格式如下:" \ - # "### 分类1" \ - # "1.#标题1" \ - # "2.#标题2" \ - # "分类之间使用--号进行分割,无内容则忽略该分组") + news - - # rsp = self.chat.get_answer(news) self.sendTextMsg(news, sender) except Exception as e: self.LOG.error(f"newsBaiduReport error:{e}") @@ -326,13 +358,9 @@ class Robot(Job): def sendEpicFreeGames(self): try: - receivers = self.config.NEWS - if not receivers: - return if is_friday(): games = get_free() - for r in receivers: - self.sendTextMsg(games, r) + self.send_group_txt_message(games, Feature.EPIC) except Exception as e: self.LOG.error(f"sendEpicFreeGames error:{e}") @@ -344,11 +372,12 @@ class Robot(Job): def generateAndSendRanking(self): try: - receivers = self.config.NEWS + receivers = self.gbm.get_group_list() if not receivers: return for r in receivers: - self.sendTextMsg(generate_and_send_ranking(r, self.allContacts), r) + if self.gbm.get_group_permission(r, Feature.DAILY_SUMMARY): + self.sendTextMsg(generate_and_send_ranking(r, self.allContacts), r) except Exception as e: self.LOG.error(f"SendRanking error:{e}") @@ -356,7 +385,7 @@ class Robot(Job): try: path = pdf_file_path() # 暂时只发4K群 - self.wcf.send_file(path, "45317011307@chatroom") + self.send_group_file_message(path, Feature.PDF_CAPABILITY) except Exception as e: self.LOG.error(f"generateSehuatangPdf error:{e}") diff --git a/sehuatang/shehuatang.py b/sehuatang/shehuatang.py index cabeb7d..c7e10b8 100644 --- a/sehuatang/shehuatang.py +++ b/sehuatang/shehuatang.py @@ -74,7 +74,6 @@ def fetch_and_create_pdf(url): # 获取今天的日期 today = datetime.now().strftime('%Y-%m-%d') - # 注册中文字体 pdfmetrics.registerFont(TTFont('SamHei', 'fonts/simhei.ttf')) # 设置中文字体路径 styles = getSampleStyleSheet() @@ -170,7 +169,6 @@ def fetch_and_create_pdf(url): # 生成PDF doc.build(content) - # 获取PDF文件的绝对路径 absolute_pdf_path = os.path.abspath(pdf_filename) print(f"PDF saved as {absolute_pdf_path}") @@ -202,12 +200,12 @@ def add_pdf_encryption(pdf_file, password="4000"): print(f"PDF加密成功,密码为: {password}") - def pdf_file_path(): url = 'https://www.sehuatang.net/forum.php?mod=forumdisplay&fid=103&filter=typeid&typeid=481' pdf_path = fetch_and_create_pdf(url) print(f"返回的PDF文件路径:{pdf_path}") return pdf_path + if __name__ == "__main__": pdf_file_path()