# 马莹:导入必要的模块 import logging # 日志模块,用于记录系统运行日志 import os # 操作系统接口模块,用于处理文件路径等 import re # 正则表达式模块,用于字符串匹配 import jsonpickle # 用于复杂对象的JSON序列化与反序列化 from django.conf import settings # 导入Django项目设置 from werobot import WeRoBot # 导入微信机器人框架 from werobot.replies import ArticlesReply, Article # 导入微信消息回复类型 from werobot.session.filestorage import FileStorage # 导入文件存储会话的类 from djangoblog.utils import get_sha256 # 导入自定义的SHA256加密工具 from servermanager.api.blogapi import BlogApi # 导入博客相关API from servermanager.api.commonapi import ChatGPT, CommandHandler # 导入聊天机器人和命令处理工具 from .MemcacheStorage import MemcacheStorage # 导入自定义的Memcache会话存储类 # 马莹:初始化微信机器人,从环境变量获取token,默认使用'lylinux',启用会话功能 robot = WeRoBot(token=os.environ.get('DJANGO_WEROBOT_TOKEN') or 'lylinux', enable_session=True) # 马莹:实例化Memcache存储对象 memstorage = MemcacheStorage() # 马莹:检查Memcache是否可用,可用则使用Memcache存储会话,否则使用文件存储 if memstorage.is_available: robot.config['SESSION_STORAGE'] = memstorage else: # 马莹:若文件存储的会话文件已存在则删除 if os.path.exists(os.path.join(settings.BASE_DIR, 'werobot_session')): os.remove(os.path.join(settings.BASE_DIR, 'werobot_session')) # 马莹:使用文件存储会话 robot.config['SESSION_STORAGE'] = FileStorage(filename='werobot_session') # 马莹:实例化博客API、命令处理器和日志记录器 blogapi = BlogApi() cmd_handler = CommandHandler() logger = logging.getLogger(__name__) def convert_to_article_reply(articles, message): """ 将文章列表转换为微信图文消息回复 :param articles: 文章对象列表 :param message: 接收的消息对象 :return: 图文消息回复对象 """ reply = ArticlesReply(message=message) # 马莹:导入自定义的模板标签用于截断文章内容 from blog.templatetags.blog_tags import truncatechars_content for post in articles: # 马莹:从文章内容中提取第一张图片作为图文消息的封面图 imgs = re.findall(r'(?:http\:|https\:)?\/\/.*\.(?:png|jpg)', post.body) imgurl = '' if imgs: imgurl = imgs[0] # 马莹:创建文章对象并添加到回复中 article = Article( title=post.title, description=truncatechars_content(post.body), # 马莹:截断文章内容作为描述 img=imgurl, url=post.get_full_url() # 马莹:文章的完整链接 ) reply.add_article(article) return reply @robot.filter(re.compile(r"^\?.*")) def search(message, session): """ 处理文章搜索功能,匹配以?开头的消息 :param message: 接收的消息对象 :param session: 会话对象 :return: 搜索结果的图文回复或提示信息 """ s = message.content searchstr = str(s).replace('?', '') # 马莹:提取搜索关键词(去除开头的?) result = blogapi.search_articles(searchstr) # 马莹:调用博客API搜索文章 if result: # 马莹:将搜索结果转换为图文回复 articles = list(map(lambda x: x.object, result)) reply = convert_to_article_reply(articles, message) return reply else: return '没有找到相关文章。' @robot.filter(re.compile(r'^category\s*$', re.I)) def category(message, session): """ 处理获取文章分类目录的功能,匹配category(不区分大小写) :param message: 接收的消息对象 :param session: 会话对象 :return: 分类目录字符串 """ categorys = blogapi.get_category_lists() # 马莹:调用博客API获取分类列表 # 马莹:将分类名称拼接为字符串返回 content = ','.join(map(lambda x: x.name, categorys)) return '所有文章分类目录:' + content @robot.filter(re.compile(r'^recent\s*$', re.I)) def recents(message, session): """ 处理获取最新文章的功能,匹配recent(不区分大小写) :param message: 接收的消息对象 :param session: 会话对象 :return: 最新文章的图文回复或提示信息 """ articles = blogapi.get_recent_articles() # 马莹:调用博客API获取最新文章 if articles: # 马莹:将最新文章转换为图文回复 reply = convert_to_article_reply(articles, message) return reply else: return "暂时还没有文章" @robot.filter(re.compile('^help$', re.I)) def help(message, session): """ 处理帮助信息查询,匹配help(不区分大小写) :param message: 接收的消息对象 :param session: 会话对象 :return: 帮助信息字符串 """ return '''欢迎关注! 默认会与图灵机器人聊天~~ 你可以通过下面这些命令来获得信息 ?关键字搜索文章. 如?python. category获得文章分类目录及文章数. category-***获得该分类目录文章 如category-python recent获得最新文章 help获得帮助. weather:获得天气 如weather:西安 idcard:获得身份证信息 如idcard:61048119xxxxxxxxxx music:音乐搜索 如music:阴天快乐 PS:以上标点符号都不支持中文标点~~ ''' @robot.filter(re.compile(r'^weather\:.*$', re.I)) def weather(message, session): """ 处理天气查询功能(建设中),匹配以weather:开头的消息(不区分大小写) :param message: 接收的消息对象 :param session: 会话对象 :return: 建设中提示 """ return "建设中..." @robot.filter(re.compile(r'^idcard\:.*$', re.I)) def idcard(message, session): """ 处理身份证信息查询功能(建设中),匹配以idcard:开头的消息(不区分大小写) :param message: 接收的消息对象 :param session: 会话对象 :return: 建设中提示 """ return "建设中..." @robot.handler def echo(message, session): """ 通用消息处理器,处理未被上述过滤器匹配的消息 :param message: 接收的消息对象 :param session: 会话对象 :return: 消息处理结果 """ handler = MessageHandler(message, session) return handler.handler() class MessageHandler: """消息处理类,处理用户消息并返回相应结果""" def __init__(self, message, session): """ 初始化方法 :param message: 接收的消息对象 :param session: 会话对象 """ userid = message.source # 获取用户ID self.message = message self.session = session self.userid = userid try: # 马莹:从会话中获取用户信息,反序列化为对象 info = session[userid] self.userinfo = jsonpickle.decode(info) except Exception as e: # 马莹:若会话中无用户信息,则初始化新的用户信息对象 userinfo = WxUserInfo() self.userinfo = userinfo @property def is_admin(self): """判断当前用户是否为管理员""" return self.userinfo.isAdmin @property def is_password_set(self): """判断管理员密码是否已设置""" return self.userinfo.isPasswordSet def save_session(self): """将用户信息序列化并保存到会话中""" info = jsonpickle.encode(self.userinfo) self.session[self.userid] = info def handler(self): """处理消息的核心方法""" info = self.message.content # 马莹:获取消息内容 # 马莹:管理员退出功能:若为管理员且消息为EXIT,则退出管理员模式 if self.userinfo.isAdmin and info.upper() == 'EXIT': self.userinfo = WxUserInfo() self.save_session() return "退出成功" # 马莹:进入管理员模式:消息为ADMIN时,标记用户为管理员(待验证密码) if info.upper() == 'ADMIN': self.userinfo.isAdmin = True self.save_session() return "输入管理员密码" # 马莹:管理员密码验证:若为管理员且未设置密码,则验证输入的密码 if self.userinfo.isAdmin and not self.userinfo.isPasswordSet: passwd = settings.WXADMIN # 马莹:从设置中获取管理员密码 if settings.TESTING: passwd = '123' # 马莹:测试环境下密码为123 # 马莹:验证密码(双重SHA256加密后比较) if passwd.upper() == get_sha256(get_sha256(info)).upper(): self.userinfo.isPasswordSet = True # 标记密码已设置 self.save_session() return "验证通过,请输入命令或者要执行的命令代码:输入helpme获得帮助" else: # 马莹:密码错误处理,超过3次则退出管理员模式 if self.userinfo.Count >= 3: self.userinfo = WxUserInfo() self.save_session() return "超过验证次数" self.userinfo.Count += 1 # 增加错误次数 self.save_session() return "验证失败,请重新输入管理员密码:" # 马莹:管理员命令处理:已验证的管理员可执行命令 if self.userinfo.isAdmin and self.userinfo.isPasswordSet: # 马莹:确认执行命令:若之前有命令且当前消息为Y,则执行命令 if self.userinfo.Command != '' and info.upper() == 'Y': return cmd_handler.run(self.userinfo.Command) else: # 马莹:帮助信息:输入helpme获取命令帮助 if info.upper() == 'HELPME': return cmd_handler.get_help() # 马莹:暂存命令并提示确认 self.userinfo.Command = info self.save_session() return "确认执行: " + info + " 命令?" # 马莹:普通用户消息:调用ChatGPT处理 return ChatGPT.chat(info) class WxUserInfo(): """微信用户信息类,用于存储用户会话相关信息""" def __init__(self): self.isAdmin = False # 马莹:是否为管理员 self.isPasswordSet = False # 马莹:是否已设置管理员密码 self.Count = 0 # 马莹:密码错误次数 self.Command = '' # 马莹:待执行的命令