重大版本调整:gewechat兼容。
This commit is contained in:
@@ -7,14 +7,12 @@ from queue import Empty
|
||||
from threading import Thread
|
||||
import random
|
||||
|
||||
from gewechat_client import GewechatClient
|
||||
|
||||
from base.func_doubao import Doubao
|
||||
from base.func_epic import is_friday, get_free
|
||||
from base.func_zhipu import ZhiPu
|
||||
|
||||
from wcferry import Wcf, WxMsg
|
||||
|
||||
from base.func_bard import BardAssistant
|
||||
from base.func_chatglm import ChatGLM
|
||||
from base.func_chatgpt import ChatGPT
|
||||
from base.func_news import News
|
||||
from base.func_tigerbot import TigerBot
|
||||
@@ -22,6 +20,7 @@ from base.func_xinghuo_web import XinghuoWeb
|
||||
from base.func_claude import Claude
|
||||
from configuration import Config
|
||||
from constants import ChatType
|
||||
from gewechat.call_back_message.message import WxMessage, MessageType
|
||||
from utils.wechat.message_to_db import MessageStorage
|
||||
from plugin_common.event_system import EventType, EventSystem
|
||||
from plugin_common.message_plugin_interface import MessagePluginInterface
|
||||
@@ -33,8 +32,6 @@ from job_mgmt import Job
|
||||
from utils.robot_cmd.robot_command import Feature
|
||||
from utils.robot_cmd.robot_command import PermissionStatus
|
||||
|
||||
__version__ = "39.5.1.0"
|
||||
|
||||
from sehuatang.shehuatang import pdf_file_path
|
||||
from utils.wechat.contact_manager import ContactManager
|
||||
from xiuren.meitu_dl import meitu_dowload_pub_pic
|
||||
@@ -48,17 +45,16 @@ class Robot(Job):
|
||||
"""个性化自己的机器人
|
||||
"""
|
||||
|
||||
def __init__(self, config: Config, wcf: Wcf, chat_type: int) -> None:
|
||||
self.wcf = wcf
|
||||
def __init__(self, config: Config, app_id: str, client: GewechatClient, chat_type: int) -> None:
|
||||
self.app_id = app_id
|
||||
self.client = client
|
||||
self.config = config
|
||||
self.LOG = logging.getLogger("Robot")
|
||||
|
||||
self.wxid = self.wcf.get_self_wxid()
|
||||
# 初始化联系人管理器并设置联系人
|
||||
self.contact_manager = ContactManager.get_instance()
|
||||
self.allContacts = self.get_all_contacts()
|
||||
self.all_head_img = self.get_all_head_img_url()
|
||||
self.contact_manager.set_contacts(self.allContacts, self.all_head_img, self.wcf)
|
||||
self.contact_manager.set_contacts(self.allContacts)
|
||||
|
||||
self.LOG.info(f"DB+REDIS 连接池开始初始化")
|
||||
# 使用单例模式获取实例
|
||||
@@ -73,7 +69,7 @@ class Robot(Job):
|
||||
self.redis_pool = self.db_manager.redis_pool
|
||||
|
||||
# 初始化消息工具类 - 使用联系人管理器
|
||||
self.message_util = MessageUtil(wcf, self.contact_manager)
|
||||
self.message_util = MessageUtil(app_id, client, self.contact_manager)
|
||||
self.groups = {} # 存储按group_id分组的消息列表,每个group_id最多保留10条消息
|
||||
GroupBotManager.load_local_cache()
|
||||
|
||||
@@ -89,7 +85,7 @@ class Robot(Job):
|
||||
# 设置插件系统上下文
|
||||
self.system_context = {
|
||||
"config": config,
|
||||
"wcf": wcf,
|
||||
"client": client,
|
||||
"event_system": self.event_system,
|
||||
"plugin_registry": self.plugin_registry,
|
||||
"db_pool": self.db_pool,
|
||||
@@ -105,48 +101,7 @@ class Robot(Job):
|
||||
self.LOG.info("插件系统初始化完成")
|
||||
|
||||
# 消息存档模块初始化,自动完成入库动作
|
||||
self.message_storage = MessageStorage(self.wcf)
|
||||
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)
|
||||
elif chat_type == ChatType.CHATGPT.value and ChatGPT.value_check(self.config.CHATGPT):
|
||||
self.chat = ChatGPT(self.config.CHATGPT)
|
||||
elif chat_type == ChatType.XINGHUO_WEB.value and XinghuoWeb.value_check(self.config.XINGHUO_WEB):
|
||||
self.chat = XinghuoWeb(self.config.XINGHUO_WEB)
|
||||
elif chat_type == ChatType.CHATGLM.value and ChatGLM.value_check(self.config.CHATGLM):
|
||||
self.chat = ChatGLM(self.config.CHATGLM)
|
||||
elif chat_type == ChatType.BardAssistant.value and BardAssistant.value_check(self.config.BardAssistant):
|
||||
self.chat = BardAssistant(self.config.BardAssistant)
|
||||
elif chat_type == ChatType.ZhiPu.value and ZhiPu.value_check(self.config.ZhiPu):
|
||||
self.chat = ZhiPu(self.config.ZhiPu)
|
||||
elif chat_type == ChatType.CLAUDE.value and Claude.value_check(self.config.CLAUDE):
|
||||
self.chat = Claude(self.config.CLAUDE)
|
||||
elif chat_type == ChatType.DOUBAO.value and Claude.value_check(self.config.DOUBAO):
|
||||
self.chat = Doubao(self.config.DOUBAO)
|
||||
else:
|
||||
self.LOG.warning("未配置模型")
|
||||
self.chat = None
|
||||
else:
|
||||
if TigerBot.value_check(self.config.TIGERBOT):
|
||||
self.chat = TigerBot(self.config.TIGERBOT)
|
||||
elif ChatGPT.value_check(self.config.CHATGPT):
|
||||
self.chat = ChatGPT(self.config.CHATGPT)
|
||||
elif XinghuoWeb.value_check(self.config.XINGHUO_WEB):
|
||||
self.chat = XinghuoWeb(self.config.XINGHUO_WEB)
|
||||
elif ChatGLM.value_check(self.config.CHATGLM):
|
||||
self.chat = ChatGLM(self.config.CHATGLM)
|
||||
elif BardAssistant.value_check(self.config.BardAssistant):
|
||||
self.chat = BardAssistant(self.config.BardAssistant)
|
||||
elif ZhiPu.value_check(self.config.ZhiPu):
|
||||
self.chat = ZhiPu(self.config.ZhiPu)
|
||||
elif Claude.value_check(self.config.CLAUDE):
|
||||
self.chat = Claude(self.config.CLAUDE)
|
||||
elif Doubao.value_check(self.config.DOUBAO):
|
||||
self.chat = Doubao(self.config.DOUBAO)
|
||||
else:
|
||||
self.LOG.warning("未配置模型")
|
||||
self.chat = None
|
||||
self.LOG.info(f"已选择: {self.chat}")
|
||||
self.message_storage = MessageStorage(self.client)
|
||||
|
||||
@staticmethod
|
||||
def value_check(args: dict) -> bool:
|
||||
@@ -154,11 +109,11 @@ class Robot(Job):
|
||||
return all(value is not None for key, value in args.items() if key != 'proxy')
|
||||
return False
|
||||
|
||||
def toChitchat(self, msg: WxMsg) -> bool:
|
||||
def toChitchat(self, msg: WxMessage) -> bool:
|
||||
"""闲聊,接入 ChatGPT
|
||||
"""
|
||||
# 去除@的人和空格等字符
|
||||
q = re.sub(r"@.*?[\u2005|\s]", "", msg.content).replace(" ", "")
|
||||
q = re.sub(r"@.*?[\u2005|\s]", "", msg.content.raw_content).replace(" ", "")
|
||||
|
||||
if q == "#今日百度新闻":
|
||||
self.news_baidu_report((msg.roomid if msg.from_group() else msg.sender))
|
||||
@@ -176,7 +131,7 @@ class Robot(Job):
|
||||
else:
|
||||
return True
|
||||
|
||||
def processMsg(self, msg: WxMsg) -> None:
|
||||
def processMsg(self, msg: WxMessage) -> None:
|
||||
"""当接收到消息的时候,会调用本方法。如果不实现本方法,则打印原始消息。
|
||||
此处可进行自定义发送的内容,如通过 msg.content 关键字自动获取当前天气信息,并发送到对应的群组@发送者
|
||||
群号:msg.roomid 微信ID:msg.sender 消息内容:msg.content
|
||||
@@ -222,7 +177,7 @@ class Robot(Job):
|
||||
try:
|
||||
self.message_storage.archive_message(msg)
|
||||
# 单独处理图片消息
|
||||
if msg.type == 3: # 图片消息类型
|
||||
if msg.msg_type == 3: # 图片消息类型
|
||||
self.message_storage.process_image(msg)
|
||||
except Exception as e:
|
||||
self.LOG.error(f"archive_message error: {e}")
|
||||
@@ -237,7 +192,7 @@ class Robot(Job):
|
||||
rsp = self.gbm.handle_command(msg.roomid, msg.content)
|
||||
# 不在群里发送,防止被骚扰
|
||||
if rsp is not None:
|
||||
self.send_text_msg(rsp, msg.roomid, msg.sender)
|
||||
self.message_util.send_text(rsp, msg.roomid, msg.sender)
|
||||
return
|
||||
except Exception as e:
|
||||
self.LOG.error(f"revoke_receive_message error: {e}")
|
||||
@@ -248,15 +203,7 @@ class Robot(Job):
|
||||
if plugin_processed:
|
||||
return
|
||||
|
||||
# 非群聊信息,按消息类型进行处理
|
||||
if msg.type == 37: # 好友请求
|
||||
self.LOG.info(f"收到好友请求:{msg}")
|
||||
self.auto_accept_friend_request(msg)
|
||||
|
||||
elif msg.type == 10000: # 系统信息
|
||||
self.say_hi_to_new_friend(msg)
|
||||
|
||||
elif msg.type == 0x01: # 文本消息
|
||||
elif msg.msg_type == MessageType.TEXT: # 文本消息
|
||||
# 让配置加载更灵活,自己可以更新配置。也可以利用定时任务更新。
|
||||
if msg.from_self():
|
||||
if msg.content == "^更新$":
|
||||
@@ -274,79 +221,15 @@ class Robot(Job):
|
||||
else:
|
||||
self.toChitchat(msg) # 闲聊
|
||||
|
||||
def onMsg(self, msg: WxMsg) -> int:
|
||||
def onMsg(self, msg: WxMessage) -> int:
|
||||
try:
|
||||
self.LOG.debug(msg) # 打印信息
|
||||
self.processMsg(msg)
|
||||
self.LOG.info(msg) # 打印信息
|
||||
# self.processMsg(msg)
|
||||
except Exception as e:
|
||||
self.LOG.error(e)
|
||||
|
||||
return 0
|
||||
|
||||
def enableRecvMsg(self) -> None:
|
||||
self.wcf.enable_recv_msg(self.onMsg)
|
||||
|
||||
def enableReceivingMsg(self) -> None:
|
||||
def innerProcessMsg(wcf: Wcf):
|
||||
while wcf.is_receiving_msg():
|
||||
try:
|
||||
msg = wcf.get_msg()
|
||||
self.LOG.debug(msg)
|
||||
self.processMsg(msg)
|
||||
except Empty:
|
||||
continue # Empty message
|
||||
except Exception as e:
|
||||
self.LOG.error(f"Receiving message error: {e}")
|
||||
|
||||
self.wcf.enable_receiving_msg()
|
||||
Thread(target=innerProcessMsg, name="GetMessage", args=(self.wcf,), daemon=True).start()
|
||||
|
||||
def send_text_msg(self, msg: str, receiver: str, at_list: str = "") -> None:
|
||||
""" 发送消息
|
||||
:param msg: 消息字符串
|
||||
:param receiver: 接收人wxid或者群id
|
||||
:param at_list: 要@的wxid, @所有人的wxid为:notify@all
|
||||
"""
|
||||
# msg 中需要有 @ 名单中一样数量的 @
|
||||
|
||||
# 风控处理,随机延迟发送,解决群消息高频发送导致的微信风险
|
||||
time.sleep(random.uniform(0.3, 1.0))
|
||||
|
||||
ats = ""
|
||||
if at_list:
|
||||
if at_list == "notify@all": # @所有人
|
||||
ats = " @所有人"
|
||||
else:
|
||||
wxids = at_list.split(",")
|
||||
for wxid in wxids:
|
||||
# 根据 wxid 查找群昵称
|
||||
ats += f" @{self.wcf.get_alias_in_chatroom(wxid, receiver)}"
|
||||
|
||||
# {msg}{ats} 表示要发送的消息内容后面紧跟@,例如 北京天气情况为:xxx @张三
|
||||
if ats == "":
|
||||
self.LOG.info(f"To {receiver}: {msg}")
|
||||
self.wcf.send_text(f"{msg}", receiver, at_list)
|
||||
else:
|
||||
self.LOG.info(f"To {receiver}: {ats}\r{msg}")
|
||||
self.wcf.send_text(f"{ats}\n\n{msg}", receiver, at_list)
|
||||
|
||||
def get_all_contacts(self) -> dict:
|
||||
"""
|
||||
获取联系人(包括好友、公众号、服务号、群成员……)
|
||||
格式: {"wxid": "NickName"}
|
||||
"""
|
||||
contacts = self.wcf.query_sql("MicroMsg.db", "SELECT UserName, NickName FROM Contact;")
|
||||
return {contact["UserName"]: contact["NickName"] for contact in contacts}
|
||||
|
||||
def get_all_head_img_url(self) -> dict:
|
||||
try:
|
||||
head_img_urls = self.wcf.query_sql("MicroMsg.db", "SELECT usrName ,bigHeadImgUrl FROM ContactHeadImgUrl;")
|
||||
# self.LOG.info(f"head_img_urls: {head_img_urls}")
|
||||
return {contact["usrName"]: contact["bigHeadImgUrl"] for contact in head_img_urls}
|
||||
except Exception as e:
|
||||
self.LOG.error(f"get_all_head_img_url error: {e}")
|
||||
return {}
|
||||
|
||||
def keep_running_and_block_process(self) -> None:
|
||||
"""
|
||||
保持机器人运行,不让进程退出
|
||||
@@ -355,31 +238,11 @@ class Robot(Job):
|
||||
self.runPendingJobs()
|
||||
time.sleep(1)
|
||||
|
||||
def auto_accept_friend_request(self, msg: WxMsg) -> None:
|
||||
try:
|
||||
xml = ET.fromstring(msg.content)
|
||||
v3 = xml.attrib["encryptusername"]
|
||||
v4 = xml.attrib["ticket"]
|
||||
scene = int(xml.attrib["scene"])
|
||||
res = self.wcf.accept_new_friend(v3, v4, scene)
|
||||
self.LOG.info(f"同意好友请求:{res}")
|
||||
except Exception as e:
|
||||
self.LOG.error(f"同意好友出错:{e}")
|
||||
|
||||
def say_hi_to_new_friend(self, msg: WxMsg) -> None:
|
||||
nickName = re.findall(r"你已添加了(.*),现在可以开始聊天了。", msg.content)
|
||||
if nickName:
|
||||
# 添加了好友,更新好友列表和联系人管理器
|
||||
self.allContacts[msg.sender] = nickName[0]
|
||||
self.contact_manager.update_contact(msg.sender, nickName[0])
|
||||
self.send_text_msg(f"Hi {nickName[0]},我自动通过了你的好友请求。", msg.sender)
|
||||
|
||||
# 添加一个方法用于刷新联系人信息
|
||||
def refresh_contacts(self):
|
||||
"""刷新联系人信息"""
|
||||
self.allContacts = self.get_all_contacts()
|
||||
self.all_head_img = self.get_all_head_img_url()
|
||||
self.contact_manager.refresh_contacts(self.allContacts, self.all_head_img, self.wcf)
|
||||
self.contact_manager.refresh_contacts(self.allContacts)
|
||||
self.LOG.info("联系人信息已刷新")
|
||||
|
||||
def send_group_txt_message(self, msg: str, feature: Feature):
|
||||
@@ -389,7 +252,7 @@ class Robot(Job):
|
||||
return
|
||||
for r in receivers:
|
||||
if self.gbm.get_group_permission(r, feature) == PermissionStatus.ENABLED:
|
||||
self.send_text_msg(msg, r)
|
||||
self.message_util.send_text(msg, r)
|
||||
except Exception as e:
|
||||
self.LOG.error(f"send_group_txt_message:{feature.description} error:{e}")
|
||||
|
||||
@@ -400,11 +263,11 @@ class Robot(Job):
|
||||
return
|
||||
for r in receivers:
|
||||
if self.gbm.get_group_permission(r, feature) == PermissionStatus.ENABLED:
|
||||
self.wcf.send_file(path, r)
|
||||
self.message_util.send_file(path, r)
|
||||
except Exception as e:
|
||||
self.LOG.error(f"send_group_file_message:{feature.description} error:{e}")
|
||||
|
||||
def process_plugin_message(self, msg: WxMsg) -> bool:
|
||||
def process_plugin_message(self, msg: WxMessage) -> bool:
|
||||
"""使用插件处理消息"""
|
||||
# 获取所有消息处理插件
|
||||
message_plugins = self.plugin_registry.get_plugins_by_type(MessagePluginInterface)
|
||||
@@ -415,16 +278,15 @@ class Robot(Job):
|
||||
continue
|
||||
|
||||
try:
|
||||
# 转换WxMsg为插件可处理的格式
|
||||
# 转换WxMessage为插件可处理的格式
|
||||
plugin_msg = {
|
||||
"type": msg.type,
|
||||
"type": msg.msg_type,
|
||||
"content": msg.content,
|
||||
"sender": msg.sender,
|
||||
"roomid": msg.roomid if msg.from_group() else "",
|
||||
"xml": msg.xml,
|
||||
"xml": msg.content.xml_content,
|
||||
"is_at": msg.is_at(self.wxid), # 确保正确设置is_at标志
|
||||
"timestamp": time.time(),
|
||||
"wcf": self.wcf, # 提供wcf对象,让插件可以直接发送消息
|
||||
"message_util": self.message_util, # 提供消息工具类
|
||||
"gbm": self.gbm, # 每次从程序变量中取,保证最新
|
||||
"all_contacts": self.allContacts,
|
||||
@@ -536,6 +398,87 @@ class Robot(Job):
|
||||
def xiu_ren_pdf_send(self):
|
||||
try:
|
||||
pub_path = generate_pdf_from_images("xiuren")
|
||||
self.wcf.send_file(pub_path, "45317011307@chatroom")
|
||||
self.message_util.send_file(pub_path, "45317011307@chatroom")
|
||||
except Exception as e:
|
||||
self.LOG.error(f"xiu_ren_pdf_send error:{e}")
|
||||
|
||||
def get_all_contacts(self) -> dict:
|
||||
"""获取所有联系人信息并保存到数据库"""
|
||||
from db.contacts_db import ContactsDBOperator
|
||||
|
||||
contacts_dict = {}
|
||||
contacts_wxids = self.client.fetch_contacts_list(self.app_id)
|
||||
|
||||
# 初始化联系人数据库操作类
|
||||
contacts_db = ContactsDBOperator()
|
||||
|
||||
for wxid in contacts_wxids:
|
||||
# 获取联系人详细信息
|
||||
contact_info = self.client.get_detail_info(self.app_id, wxid)
|
||||
|
||||
# 将联系人信息添加到字典中
|
||||
if contact_info and contact_info.get("ret") == 200 and "data" in contact_info:
|
||||
contact_data = contact_info.get("data", [])
|
||||
|
||||
# 保存联系人信息到数据库
|
||||
if contact_data:
|
||||
try:
|
||||
# 判断联系人类型
|
||||
contact_type = "friends" # 默认为好友类型
|
||||
if wxid.endswith("@chatroom"):
|
||||
contact_type = "chatrooms"
|
||||
# 如果是这个类型,则提取群成员信息
|
||||
# 提取群成员信息
|
||||
self.update_chatroom_member_details(wxid)
|
||||
elif wxid.startswith("gh_"):
|
||||
contact_type = "ghs"
|
||||
|
||||
# 保存到数据库
|
||||
contacts_db.save_contacts(contact_data, contact_type)
|
||||
|
||||
# 添加到返回字典
|
||||
for contact in contact_data:
|
||||
user_name = contact.get("userName")
|
||||
if user_name:
|
||||
contacts_dict[user_name] = contact.get("nickName") or user_name
|
||||
except Exception as e:
|
||||
self.LOG.error(f"保存联系人信息到数据库失败: {e}")
|
||||
|
||||
self.LOG.info(f"成功获取并保存{len(contacts_dict)}个联系人信息")
|
||||
return contacts_dict
|
||||
|
||||
def update_chatroom_member_details(self, chatroom_id):
|
||||
"""更新群成员详细信息"""
|
||||
try:
|
||||
# 首先获取群成员列表
|
||||
members_response = self.client.get_chatroom_member_list(self.app_id, chatroom_id)
|
||||
if members_response and members_response.get('ret') == 200:
|
||||
member_list = members_response.get('data', {}).get('memberList', [])
|
||||
|
||||
# 提取成员wxid列表
|
||||
member_wxids = [member.get('wxid') for member in member_list if member.get('wxid')]
|
||||
|
||||
if member_wxids:
|
||||
# 获取成员详细信息
|
||||
details_response = self.client.get_chatroom_member_detail(self.app_id, chatroom_id, member_wxids)
|
||||
|
||||
# 使用ContactsDBOperator处理响应
|
||||
from db.contacts_db import ContactsDBOperator
|
||||
contacts_db = ContactsDBOperator()
|
||||
success = contacts_db.process_chatroom_member_detail_response(chatroom_id, details_response)
|
||||
|
||||
if success:
|
||||
self.LOG.info(f"成功更新群聊{chatroom_id}的成员详细信息")
|
||||
else:
|
||||
self.LOG.error(f"更新群聊{chatroom_id}的成员详细信息失败")
|
||||
|
||||
return success
|
||||
else:
|
||||
self.LOG.warning(f"群聊{chatroom_id}没有成员")
|
||||
return False
|
||||
else:
|
||||
self.LOG.error(f"获取群聊{chatroom_id}成员列表失败")
|
||||
return False
|
||||
except Exception as e:
|
||||
self.LOG.error(f"更新群聊成员详细信息出错: {e}")
|
||||
return False
|
||||
|
||||
Reference in New Issue
Block a user