关注数据监听。

This commit is contained in:
liuwei
2025-04-22 11:42:22 +08:00
parent f42f26c910
commit e810958ec6
2 changed files with 55 additions and 116 deletions

View File

@@ -8,20 +8,27 @@ import logging
import json import json
from typing import List, Dict, Optional, Union, Any from typing import List, Dict, Optional, Union, Any
from db.base import BaseDBOperator
from db.connection import DBConnectionManager from db.connection import DBConnectionManager
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class ContactsDBOperator: class ContactsDBOperator(BaseDBOperator):
"""微信联系人数据库操作类""" """微信联系人数据库操作类"""
def __init__(self, db_manager: DBConnectionManager): def __init__(self, db_manager=None):
super().__init__(db_manager) """初始化联系人数据库操作类"""
super().__init__(db_manager or DBConnectionManager.get_instance())
self.logger = logging.getLogger("ContactsDBOperator")
# 确保数据库表存在
# self._ensure_table_exists()
def _ensure_table_exists(self): def _ensure_table_exists(self):
"""确保联系人表存在""" """确保联系人表存在"""
try: try:
# 创建联系人表 # 创建联系人表
sql = """ self.execute_update("""
CREATE TABLE IF NOT EXISTS t_wechat_contacts ( CREATE TABLE IF NOT EXISTS t_wechat_contacts (
id INT AUTO_INCREMENT PRIMARY KEY, id INT AUTO_INCREMENT PRIMARY KEY,
user_name VARCHAR(64) NOT NULL COMMENT '微信ID', user_name VARCHAR(64) NOT NULL COMMENT '微信ID',
@@ -49,13 +56,10 @@ class ContactsDBOperator:
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
UNIQUE KEY `idx_user_name` (`user_name`) UNIQUE KEY `idx_user_name` (`user_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='微信联系人信息表'; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='微信联系人信息表';
""" """)
conn = self.db_manager.get_connection()
cursor = conn.cursor()
cursor.execute(sql)
# 创建群成员表 - 增加了更多字段以支持详细信息 # 创建群成员表 - 增加了更多字段以支持详细信息
sql_chatroom_member = """ self.execute_update("""
CREATE TABLE IF NOT EXISTS t_chatroom_member ( CREATE TABLE IF NOT EXISTS t_chatroom_member (
id INT AUTO_INCREMENT PRIMARY KEY, id INT AUTO_INCREMENT PRIMARY KEY,
chatroom_id VARCHAR(64) NOT NULL COMMENT '群聊ID', chatroom_id VARCHAR(64) NOT NULL COMMENT '群聊ID',
@@ -84,19 +88,12 @@ class ContactsDBOperator:
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
UNIQUE KEY `idx_chatroom_member` (`chatroom_id`, `wxid`) UNIQUE KEY `idx_chatroom_member` (`chatroom_id`, `wxid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='微信群成员信息表'; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='微信群成员信息表';
""" """)
cursor.execute(sql_chatroom_member)
conn.commit() self.logger.info("成功创建或确认微信联系人表和群成员表存在")
logger.info("成功创建或确认微信联系人表和群成员表存在")
except Exception as e: except Exception as e:
logger.error(f"创建微信联系人表或群成员表失败: {e}") self.logger.error(f"创建微信联系人表或群成员表失败: {e}")
raise raise
finally:
if 'cursor' in locals():
cursor.close()
if 'conn' in locals():
self.db_manager.release_connection(conn)
def save_contacts(self, contacts_data: List[Dict], contact_type: str) -> bool: def save_contacts(self, contacts_data: List[Dict], contact_type: str) -> bool:
"""保存联系人信息到数据库 """保存联系人信息到数据库
@@ -109,13 +106,10 @@ class ContactsDBOperator:
bool: 是否成功保存 bool: 是否成功保存
""" """
if not contacts_data: if not contacts_data:
logger.warning(f"没有{contact_type}类型的联系人数据需要保存") self.logger.warning(f"没有{contact_type}类型的联系人数据需要保存")
return True return True
try: try:
conn = self.db_manager.get_connection()
cursor = conn.cursor()
for contact in contacts_data: for contact in contacts_data:
# 将驼峰命名转换为下划线命名 # 将驼峰命名转换为下划线命名
data = { data = {
@@ -156,22 +150,14 @@ class ContactsDBOperator:
ON DUPLICATE KEY UPDATE {update_clause} ON DUPLICATE KEY UPDATE {update_clause}
""" """
cursor.execute(sql, values) self.execute_update(sql, values)
conn.commit() self.logger.info(f"成功保存{len(contacts_data)}{contact_type}类型的联系人")
logger.info(f"成功保存{len(contacts_data)}{contact_type}类型的联系人")
return True return True
except Exception as e: except Exception as e:
logger.error(f"保存{contact_type}类型的联系人失败: {e}") self.logger.error(f"保存{contact_type}类型的联系人失败: {e}")
if 'conn' in locals():
conn.rollback()
return False return False
finally:
if 'cursor' in locals():
cursor.close()
if 'conn' in locals():
self.db_manager.release_connection(conn)
def save_simple_contacts(self, contact_list: List[str], contact_type: str) -> bool: def save_simple_contacts(self, contact_list: List[str], contact_type: str) -> bool:
"""保存简单联系人列表只有user_name到数据库 """保存简单联系人列表只有user_name到数据库
@@ -184,13 +170,10 @@ class ContactsDBOperator:
bool: 是否成功保存 bool: 是否成功保存
""" """
if not contact_list: if not contact_list:
logger.warning(f"没有{contact_type}类型的联系人数据需要保存") self.logger.warning(f"没有{contact_type}类型的联系人数据需要保存")
return True return True
try: try:
conn = self.db_manager.get_connection()
cursor = conn.cursor()
for user_name in contact_list: for user_name in contact_list:
# 构建SQL语句 # 构建SQL语句
sql = """ sql = """
@@ -199,22 +182,14 @@ class ContactsDBOperator:
ON DUPLICATE KEY UPDATE type = VALUES(type), update_time = CURRENT_TIMESTAMP ON DUPLICATE KEY UPDATE type = VALUES(type), update_time = CURRENT_TIMESTAMP
""" """
cursor.execute(sql, (user_name, contact_type)) self.execute_update(sql, (user_name, contact_type))
conn.commit() self.logger.info(f"成功保存{len(contact_list)}{contact_type}类型的简单联系人")
logger.info(f"成功保存{len(contact_list)}{contact_type}类型的简单联系人")
return True return True
except Exception as e: except Exception as e:
logger.error(f"保存{contact_type}类型的简单联系人失败: {e}") self.logger.error(f"保存{contact_type}类型的简单联系人失败: {e}")
if 'conn' in locals():
conn.rollback()
return False return False
finally:
if 'cursor' in locals():
cursor.close()
if 'conn' in locals():
self.db_manager.release_connection(conn)
def get_contacts_by_type(self, contact_type: str) -> List[Dict]: def get_contacts_by_type(self, contact_type: str) -> List[Dict]:
"""根据类型获取联系人列表 """根据类型获取联系人列表
@@ -226,27 +201,17 @@ class ContactsDBOperator:
List[Dict]: 联系人列表 List[Dict]: 联系人列表
""" """
try: try:
conn = self.db_manager.get_connection()
cursor = conn.cursor(dictionary=True)
sql = """ sql = """
SELECT * FROM t_wechat_contacts SELECT * FROM t_wechat_contacts
WHERE type = %s WHERE type = %s
ORDER BY nick_name ORDER BY nick_name
""" """
cursor.execute(sql, (contact_type,)) results = self.execute_query(sql, (contact_type,))
results = cursor.fetchall()
return results return results
except Exception as e: except Exception as e:
logger.error(f"获取{contact_type}类型的联系人失败: {e}") self.logger.error(f"获取{contact_type}类型的联系人失败: {e}")
return [] return []
finally:
if 'cursor' in locals():
cursor.close()
if 'conn' in locals():
self.db_manager.release_connection(conn)
def get_contact_by_user_name(self, user_name: str) -> Optional[Dict]: def get_contact_by_user_name(self, user_name: str) -> Optional[Dict]:
"""根据user_name获取联系人信息 """根据user_name获取联系人信息
@@ -258,27 +223,17 @@ class ContactsDBOperator:
Optional[Dict]: 联系人信息如果不存在则返回None Optional[Dict]: 联系人信息如果不存在则返回None
""" """
try: try:
conn = self.db_manager.get_connection()
cursor = conn.cursor(dictionary=True)
sql = """ sql = """
SELECT * FROM t_wechat_contacts SELECT * FROM t_wechat_contacts
WHERE user_name = %s WHERE user_name = %s
LIMIT 1 LIMIT 1
""" """
cursor.execute(sql, (user_name,)) result = self.execute_query(sql, (user_name,), fetch_one=True)
result = cursor.fetchone()
return result return result
except Exception as e: except Exception as e:
logger.error(f"获取联系人{user_name}失败: {e}") self.logger.error(f"获取联系人{user_name}失败: {e}")
return None return None
finally:
if 'cursor' in locals():
cursor.close()
if 'conn' in locals():
self.db_manager.release_connection(conn)
def get_display_name(self, user_name: str) -> str: def get_display_name(self, user_name: str) -> str:
"""获取联系人的显示名称优先使用备注其次是昵称最后是微信ID """获取联系人的显示名称优先使用备注其次是昵称最后是微信ID
@@ -302,30 +257,24 @@ class ContactsDBOperator:
Dict[str, str]: 联系人ID到显示名称的映射 Dict[str, str]: 联系人ID到显示名称的映射
""" """
try: try:
conn = self.db_manager.get_connection()
cursor = conn.cursor()
sql = """ sql = """
SELECT user_name, remark, nick_name FROM t_wechat_contacts SELECT user_name, remark, nick_name FROM t_wechat_contacts
""" """
cursor.execute(sql) results = self.execute_query(sql)
results = cursor.fetchall()
name_map = {} name_map = {}
for user_name, remark, nick_name in results: for result in results:
user_name = result.get('user_name')
remark = result.get('remark')
nick_name = result.get('nick_name')
display_name = remark or nick_name or user_name display_name = remark or nick_name or user_name
name_map[user_name] = display_name name_map[user_name] = display_name
return name_map return name_map
except Exception as e: except Exception as e:
logger.error(f"获取所有联系人名称映射失败: {e}") self.logger.error(f"获取所有联系人名称映射失败: {e}")
return {} return {}
finally:
if 'cursor' in locals():
cursor.close()
if 'conn' in locals():
self.db_manager.release_connection(conn)
def save_chatroom_member_detail(self, chatroom_id: str, member_details: List[Dict]) -> bool: def save_chatroom_member_detail(self, chatroom_id: str, member_details: List[Dict]) -> bool:
"""保存群成员详细信息到数据库 """保存群成员详细信息到数据库
@@ -338,20 +287,18 @@ class ContactsDBOperator:
bool: 是否成功保存 bool: 是否成功保存
""" """
if not member_details or not chatroom_id: if not member_details or not chatroom_id:
logger.warning(f"没有群聊{chatroom_id}的成员详细信息需要保存") self.logger.warning(f"没有群聊{chatroom_id}的成员详细信息需要保存")
return False return False
try: try:
conn = self.db_manager.get_connection()
cursor = conn.cursor()
# 获取现有的群成员信息,以便更新而不是替换 # 获取现有的群成员信息,以便更新而不是替换
existing_members_sql = """ existing_members_sql = """
SELECT wxid, is_owner, is_admin FROM t_chatroom_member SELECT wxid, is_owner, is_admin FROM t_chatroom_member
WHERE chatroom_id = %s WHERE chatroom_id = %s
""" """
cursor.execute(existing_members_sql, (chatroom_id,)) existing_members_result = self.execute_query(existing_members_sql, (chatroom_id,))
existing_members = {row[0]: (row[1], row[2]) for row in cursor.fetchall()} existing_members = {row.get('wxid'): (row.get('is_owner'), row.get('is_admin'))
for row in existing_members_result}
for member in member_details: for member in member_details:
wxid = member.get('userName', '') wxid = member.get('userName', '')
@@ -407,22 +354,14 @@ class ContactsDBOperator:
VALUES ({placeholders}) VALUES ({placeholders})
""" """
cursor.execute(sql, values) self.execute_update(sql, values)
conn.commit() self.logger.info(f"成功保存群聊{chatroom_id}{len(member_details)}个成员详细信息")
logger.info(f"成功保存群聊{chatroom_id}{len(member_details)}个成员详细信息")
return True return True
except Exception as e: except Exception as e:
logger.error(f"保存群聊{chatroom_id}的成员详细信息失败: {e}") self.logger.error(f"保存群聊{chatroom_id}的成员详细信息失败: {e}")
if 'conn' in locals():
conn.rollback()
return False return False
finally:
if 'cursor' in locals():
cursor.close()
if 'conn' in locals():
self.db_manager.release_connection(conn)
def process_chatroom_member_detail_response(self, chatroom_id: str, response: Dict) -> bool: def process_chatroom_member_detail_response(self, chatroom_id: str, response: Dict) -> bool:
"""处理获取群成员详情的API响应 """处理获取群成员详情的API响应
@@ -436,16 +375,16 @@ class ContactsDBOperator:
""" """
try: try:
if response.get('ret') != 200: if response.get('ret') != 200:
logger.error(f"获取群聊{chatroom_id}成员详情失败: {response.get('msg')}") self.logger.error(f"获取群聊{chatroom_id}成员详情失败: {response.get('msg')}")
return False return False
data = response.get('data', []) data = response.get('data', [])
if not data: if not data:
logger.warning(f"群聊{chatroom_id}成员详情数据为空") self.logger.warning(f"群聊{chatroom_id}成员详情数据为空")
return False return False
return self.save_chatroom_member_detail(chatroom_id, data) return self.save_chatroom_member_detail(chatroom_id, data)
except Exception as e: except Exception as e:
logger.error(f"处理群聊{chatroom_id}成员详情数据失败: {e}") self.logger.error(f"处理群聊{chatroom_id}成员详情数据失败: {e}")
return False return False

22
main.py
View File

@@ -109,17 +109,17 @@ def main(chat_type: int):
print(f"已将新的APP_ID: {app_id} 写入配置文件") print(f"已将新的APP_ID: {app_id} 写入配置文件")
# 同时更新当前配置对象中的APP_ID # 同时更新当前配置对象中的APP_ID
config.APP_ID = app_id config.APP_ID = app_id
#
# # 创建机器人实例 # 创建机器人实例
# robot = Robot(config, app_id, client, chat_type) robot = Robot(config, app_id, client, chat_type)
# robot.LOG.info(f"WeChatRobot gewechat 成功启动···") robot.LOG.info(f"WeChatRobot gewechat 成功启动···")
#
# # # 注册Robot实例到callback模块 # # 注册Robot实例到callback模块
# # from gewechat.api.callback import register_robot from gewechat.api.callback import register_robot
# # register_robot(app_id, robot) register_robot(app_id, robot)
#
# # 机器人启动发送测试消息 # 机器人启动发送测试消息
# client.post_text(app_id, send_msg_wxid, "gewechat client 启动成功!") client.post_text(app_id, send_msg_wxid, "gewechat client 启动成功!")
# #
# # 每天 8:30 发送新闻 # # 每天 8:30 发送新闻
# robot.onEveryTime("08:30", robot.news_baidu_report_auto) # robot.onEveryTime("08:30", robot.news_baidu_report_auto)