重大版本调整:gewechat兼容。
This commit is contained in:
@@ -143,7 +143,7 @@ def plugin_points_cost(points: int, description: str = None, feature: Feature =
|
||||
# 积分不足
|
||||
wcf = message.get("wcf")
|
||||
if wcf:
|
||||
wcf.send_text(
|
||||
self.message_util.send_text(
|
||||
f"❌ 积分不足\n无法使用 {plugin_name} 功能\n"
|
||||
f"🪙 先参与积分活动[签到,答题/t]赚取吧!\n"
|
||||
f"💰 有: {user_points['total_points']} | 需: {points} |差: {points - user_points['total_points']} ",
|
||||
@@ -170,7 +170,7 @@ def plugin_points_cost(points: int, description: str = None, feature: Feature =
|
||||
response += f"\n\n💰 已消费 {points} 积分"
|
||||
wcf = message.get("wcf")
|
||||
if wcf:
|
||||
wcf.send_text(
|
||||
self.message_util.send_text(
|
||||
f"💰消费 {points} 积分",
|
||||
(roomid if roomid else sender), sender
|
||||
)
|
||||
|
||||
131
utils/json_converter.py
Normal file
131
utils/json_converter.py
Normal file
@@ -0,0 +1,131 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
JSON数据转换工具
|
||||
用于将JSON数据转换为Python对象
|
||||
"""
|
||||
|
||||
class DictObject:
|
||||
"""将字典转换为对象,使得可以通过属性访问"""
|
||||
def __init__(self, data):
|
||||
for key, value in data.items():
|
||||
if isinstance(value, dict):
|
||||
setattr(self, key, DictObject(value))
|
||||
elif isinstance(value, list):
|
||||
setattr(self, key, [
|
||||
DictObject(item) if isinstance(item, dict) else item
|
||||
for item in value
|
||||
])
|
||||
else:
|
||||
setattr(self, key, value)
|
||||
|
||||
def __repr__(self):
|
||||
"""打印对象时的表示形式"""
|
||||
attrs = ', '.join(f"{key}={repr(value)}" for key, value in self.__dict__.items())
|
||||
return f"{self.__class__.__name__}({attrs})"
|
||||
|
||||
def to_dict(self):
|
||||
"""将对象转换回字典"""
|
||||
result = {}
|
||||
for key, value in self.__dict__.items():
|
||||
if isinstance(value, DictObject):
|
||||
result[key] = value.to_dict()
|
||||
elif isinstance(value, list):
|
||||
result[key] = [
|
||||
item.to_dict() if isinstance(item, DictObject) else item
|
||||
for item in value
|
||||
]
|
||||
else:
|
||||
result[key] = value
|
||||
return result
|
||||
|
||||
|
||||
def json_to_object(json_data):
|
||||
"""
|
||||
将JSON数据转换为Python对象
|
||||
|
||||
Args:
|
||||
json_data: JSON字符串或字典
|
||||
|
||||
Returns:
|
||||
DictObject: 转换后的Python对象
|
||||
"""
|
||||
import json
|
||||
|
||||
# 如果输入是字符串,则解析为字典
|
||||
if isinstance(json_data, str):
|
||||
data = json.loads(json_data)
|
||||
else:
|
||||
data = json_data
|
||||
|
||||
# 转换为对象
|
||||
return DictObject(data)
|
||||
|
||||
|
||||
# 使用示例
|
||||
if __name__ == "__main__":
|
||||
# 示例JSON数据
|
||||
example_json = {
|
||||
"ret": 200,
|
||||
"msg": "操作成功",
|
||||
"data": {
|
||||
"friends": [
|
||||
"tmessage",
|
||||
"medianote",
|
||||
"qmessage",
|
||||
"qqmail",
|
||||
"wxid_910acevfm2nb21",
|
||||
"qqsafe",
|
||||
"wxid_9299552988412",
|
||||
"weixin",
|
||||
"exmail_tool",
|
||||
"wxid_mp05xmje0ctn22",
|
||||
"wxid_09oq4f4j4wg912",
|
||||
"wxid_6bfguz79h8n122",
|
||||
"wxid_lyuq4hr4lrjq22",
|
||||
"wxid_a1zqyljsrsdu12",
|
||||
"wxid_lv3pb3zhna3522",
|
||||
"wxid_k2biq6fuinsr22",
|
||||
"wxid_ujredjhxz9y712",
|
||||
"wxid_uwb7989u0jea12",
|
||||
"wxid_in46ey732vxu12",
|
||||
"wxid_3rvervwohj6921",
|
||||
"wxid_4wkls7tu62ua12",
|
||||
"wxid_g0bdknnotx2f12",
|
||||
"wxid_ce5fgp0icb3y21",
|
||||
"wxid_1482424825211",
|
||||
"wxid_vw3p4f6jy7bm12",
|
||||
"wxid_o2m8xm71c23522",
|
||||
"wxid_bclqpc2ho6o412",
|
||||
"wxid_98pjjzpiisi721",
|
||||
"wxid_noq2wsn5c8h222"
|
||||
],
|
||||
"chatrooms": [
|
||||
"2180313478@chatroom",
|
||||
"14358945067@chatroom",
|
||||
"17362526147@chatroom",
|
||||
"11685224357@chatroom",
|
||||
"17522822550@chatroom"
|
||||
],
|
||||
"ghs": [
|
||||
"gh_7aac992b0363",
|
||||
"gh_d7293b5f14f4",
|
||||
"gh_f51ce3ef83a4",
|
||||
"gh_7d20df86e26b",
|
||||
"gh_69bfb92a3e43"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
# 转换为对象
|
||||
obj = json_to_object(example_json)
|
||||
|
||||
# 通过属性访问
|
||||
print(f"返回码: {obj.ret}")
|
||||
print(f"消息: {obj.msg}")
|
||||
print(f"好友数量: {len(obj.data.friends)}")
|
||||
print(f"第一个好友: {obj.data.friends[0]}")
|
||||
print(f"第一个群聊: {obj.data.chatrooms[0]}")
|
||||
|
||||
# 转换回字典
|
||||
dict_data = obj.to_dict()
|
||||
print(f"转换回字典: {dict_data}")
|
||||
@@ -41,7 +41,7 @@ class Feature(Enum):
|
||||
VIDEO = 14, "黑丝视频 [黑丝视频, 黑丝, 来个黑丝,搞个黑丝]"
|
||||
VIDEO_MAN = 15, "肌肉视频 [猛男, 肌肉, 帅哥]"
|
||||
# GROUP_ADD = 16, "加群提醒"
|
||||
# DOUYIN_PARSER = 17, "抖音链接转视频"
|
||||
DOUYIN_PARSER = 17, "抖音链接转视频"
|
||||
GROUP_MEMBER_CHANGE = 18, "群成员变更提醒功能"
|
||||
# KID_PHOTO_EXTRACT = 19, "儿童照片提取转发功能" # 小朋友照片提取功能
|
||||
NEWS = 20, "全球政治经济新闻"
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
import logging
|
||||
from typing import Dict, Optional, List, Tuple
|
||||
|
||||
from wcferry import Wcf
|
||||
from gewechat_client import GewechatClient
|
||||
|
||||
from utils.json_converter import json_to_object
|
||||
|
||||
|
||||
class ContactManager:
|
||||
@@ -50,7 +52,7 @@ class ContactManager:
|
||||
cls._instance = ContactManager()
|
||||
return cls._instance
|
||||
|
||||
def set_contacts(self, contacts: Dict[str, str], head_imgs: Dict[str, str], wcf: Wcf) -> None:
|
||||
def set_contacts(self, contacts: Dict[str, str]) -> None:
|
||||
"""设置联系人字典
|
||||
|
||||
Args:
|
||||
@@ -67,13 +69,12 @@ class ContactManager:
|
||||
"gender": gender}
|
||||
"""
|
||||
self._contacts = contacts
|
||||
self._friends = wcf.get_friends()
|
||||
self._head_images = head_imgs
|
||||
self._friends = contacts
|
||||
self._logger.info(f"联系人信息已更新,共 {len(contacts)} 个联系人")
|
||||
# 分类联系人
|
||||
self._classify_contacts(wcf)
|
||||
self._classify_contacts()
|
||||
|
||||
def _classify_contacts(self, wcf: Wcf) -> None:
|
||||
def _classify_contacts(self) -> None:
|
||||
"""将联系人分类为群组、个人联系人、公共好友和公众号"""
|
||||
self._group_contacts = {}
|
||||
self._personal_contacts = {}
|
||||
@@ -90,8 +91,6 @@ class ContactManager:
|
||||
# 判断是否为群组(wxid以@chatroom结尾)
|
||||
elif wxid.endswith('@chatroom'):
|
||||
self._group_contacts[wxid] = nickname
|
||||
# 如果是群,这处理群列表内容
|
||||
self._group_contacts_friends[wxid] = wcf.get_chatroom_members(wxid)
|
||||
|
||||
# # 其他为普通好友和群成员
|
||||
# else:
|
||||
@@ -216,7 +215,7 @@ class ContactManager:
|
||||
self._personal_contacts[wxid] = nickname
|
||||
self._logger.debug(f"已更新联系人: {wxid} -> {nickname}")
|
||||
|
||||
def refresh_contacts(self, new_contacts: Dict[str, str], head_imgs: Dict[str, str], wcf: Wcf) -> None:
|
||||
def refresh_contacts(self, new_contacts: Dict[str, str]) -> None:
|
||||
"""刷新联系人信息
|
||||
|
||||
Args:
|
||||
@@ -235,11 +234,9 @@ class ContactManager:
|
||||
# "province": cnt.get("province", ""),
|
||||
# "city": cnt.get("city", ""),
|
||||
# "gender": gender}
|
||||
self._friends = wcf.get_friends()
|
||||
self._head_images = head_imgs
|
||||
self._friends = self.message_util.get_friends()
|
||||
self._logger.info(f"联系人信息已刷新,共 {len(new_contacts)} 个联系人")
|
||||
# 重新分类联系人
|
||||
self._classify_contacts(wcf)
|
||||
self._classify_contacts()
|
||||
|
||||
def get_contact_statistics(self) -> Tuple[int, int, int, int, int]:
|
||||
"""获取联系人统计信息
|
||||
|
||||
@@ -3,12 +3,15 @@ import xml.etree.ElementTree as ET
|
||||
import logging
|
||||
import concurrent.futures # 添加线程池支持
|
||||
import os
|
||||
from wcferry import WxMsg, Wcf
|
||||
|
||||
from gewechat_client import GewechatClient
|
||||
|
||||
from db.connection import DBConnectionManager
|
||||
from db.message_storage import MessageStorageDB
|
||||
# 导入积分系统
|
||||
from db.points_db import PointsDBOperator, PointSource
|
||||
from gewechat.call_back_message.message import WxMessage
|
||||
|
||||
# 配置日志
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
@@ -19,11 +22,11 @@ logger = logging.getLogger("MessageStorage")
|
||||
|
||||
class MessageStorage:
|
||||
|
||||
def __init__(self, wcf: Wcf = None):
|
||||
def __init__(self, client: GewechatClient = None):
|
||||
# 获取数据库连接管理器的单例
|
||||
self.db_manager = DBConnectionManager.get_instance()
|
||||
self.message_db = MessageStorageDB(self.db_manager)
|
||||
|
||||
|
||||
self.points_db = PointsDBOperator(self.db_manager)
|
||||
# 初始化本地缓存字典,使用 group_id 作为键
|
||||
self.local_membercounts = {}
|
||||
@@ -34,7 +37,7 @@ class MessageStorage:
|
||||
self.pending_tasks = []
|
||||
|
||||
# 保存WCF实例,用于图片处理
|
||||
self.wcf = wcf
|
||||
self.client = client
|
||||
|
||||
# 图片处理相关初始化
|
||||
self.image_executor = concurrent.futures.ThreadPoolExecutor(max_workers=3) # 专用于图片处理的线程池
|
||||
@@ -46,7 +49,7 @@ class MessageStorage:
|
||||
os.makedirs(self.image_dir, exist_ok=True)
|
||||
logger.info(f"图片存储目录: {self.image_dir}")
|
||||
|
||||
def process_message(self, message: WxMsg):
|
||||
def process_message(self, message: WxMessage):
|
||||
# 示例message字符串
|
||||
current_date = datetime.now().strftime('%Y-%m-%d')
|
||||
# 生成Redis key
|
||||
@@ -59,7 +62,7 @@ class MessageStorage:
|
||||
redis_conn.expire(key, 86400 * 2)
|
||||
# 或者使用字符串:r.incr(key) # 如果只存储一个整数值,字符串类型可能更简单
|
||||
|
||||
def archive_message(self, msg: WxMsg):
|
||||
def archive_message(self, msg: WxMessage):
|
||||
"""异步存档消息,防止堵塞主线程"""
|
||||
# 提交任务到线程池
|
||||
future = self.executor.submit(self._archive_message_task, msg)
|
||||
@@ -70,7 +73,7 @@ class MessageStorage:
|
||||
# 清理已完成的任务
|
||||
self._cleanup_completed_tasks()
|
||||
|
||||
def _archive_message_task(self, msg: WxMsg):
|
||||
def _archive_message_task(self, msg: WxMessage):
|
||||
"""实际执行消息存档的任务函数"""
|
||||
try:
|
||||
# 使用 MessageStorageDB 类存档消息
|
||||
@@ -80,7 +83,7 @@ class MessageStorage:
|
||||
'roomid': msg.roomid,
|
||||
'sender': msg.sender,
|
||||
'content': msg.content, # 添加消息内容
|
||||
'message_id': msg.id # 添加消息ID
|
||||
'message_id': msg.msg_id # 添加消息ID
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"存档消息出错: {e}")
|
||||
@@ -89,13 +92,13 @@ class MessageStorage:
|
||||
'roomid': msg.roomid,
|
||||
'sender': msg.sender,
|
||||
'content': msg.content, # 添加消息内容
|
||||
'message_id': msg.id, # 添加消息ID
|
||||
'message_id': msg.msg_id, # 添加消息ID
|
||||
'error': str(e)
|
||||
}
|
||||
|
||||
def process_image(self, msg: WxMsg):
|
||||
def process_image(self, msg: WxMessage):
|
||||
"""异步处理图片消息,与消息存档分离"""
|
||||
if msg.type != 3 or not self.wcf: # 不是图片消息或没有WCF实例
|
||||
if msg.msg_type != 3 or not self.client: # 不是图片消息或没有WCF实例
|
||||
return False
|
||||
|
||||
# 提交任务到图片处理线程池
|
||||
@@ -108,11 +111,11 @@ class MessageStorage:
|
||||
self._cleanup_completed_tasks()
|
||||
return True
|
||||
|
||||
def _process_image_task(self, msg: WxMsg):
|
||||
def _process_image_task(self, msg: WxMessage):
|
||||
"""实际执行图片处理的任务函数"""
|
||||
try:
|
||||
# 使用wcf下载图片,确保图片存在
|
||||
if self.wcf and msg.id:
|
||||
if self.client and msg.msg_id:
|
||||
# 创建按群ID或个人wxid分割的目录
|
||||
target_dir = os.path.join(self.image_dir, msg.roomid if msg.roomid else msg.sender)
|
||||
# 确保目标目录存在
|
||||
@@ -120,16 +123,27 @@ class MessageStorage:
|
||||
os.makedirs(target_dir, exist_ok=True)
|
||||
|
||||
# 尝试使用wcf下载图片到分组后的目录
|
||||
download_path = self.wcf.download_image(msg.id, msg.extra, target_dir)
|
||||
json = self.client.download_image(msg.msg_id, msg.content.xml_content, 1)
|
||||
# {
|
||||
# "ret": 200,
|
||||
# "msg": "操作成功",
|
||||
# "data": {
|
||||
# "fileUrl": "/download/20240720/wx_BTVoJ_o_r6DpxNCNiycFE/0ca5b675-8e2c-4dc1-b288-3c44a40086ec4"
|
||||
# }
|
||||
# }
|
||||
# 解析JSON
|
||||
if json and json.get('data') and json['data'].get('fileUrl'):
|
||||
file_url = json['data']['fileUrl']
|
||||
download_path = self.download_file_from_url(file_url, target_dir)
|
||||
if download_path:
|
||||
logger.info(f"使用wcf下载图片成功: {msg.id} -> {download_path}")
|
||||
logger.info(f"使用wcf下载图片成功: {msg.msg_id} -> {download_path}")
|
||||
|
||||
# 直接使用下载后的路径更新数据库
|
||||
self.message_db.update_message_image_path(msg.id, download_path)
|
||||
self.message_db.update_message_image_path(msg.msg_id, download_path)
|
||||
|
||||
return {
|
||||
'success': True,
|
||||
'message_id': msg.id,
|
||||
'message_id': msg.msg_id,
|
||||
'roomid': msg.roomid,
|
||||
'sender': msg.sender,
|
||||
'file_path': download_path
|
||||
@@ -137,7 +151,7 @@ class MessageStorage:
|
||||
else:
|
||||
return {
|
||||
'success': False,
|
||||
'message_id': msg.id,
|
||||
'message_id': msg.msg_id,
|
||||
'roomid': msg.roomid,
|
||||
'sender': msg.sender,
|
||||
'error': "图片下载失败"
|
||||
@@ -145,21 +159,25 @@ class MessageStorage:
|
||||
else:
|
||||
return {
|
||||
'success': False,
|
||||
'message_id': msg.id,
|
||||
'message_id': msg.msg_id,
|
||||
'roomid': msg.roomid,
|
||||
'sender': msg.sender,
|
||||
'error': "WCF实例不存在或消息ID无效"
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"图片处理出错: {msg.id}, 错误: {e}")
|
||||
logger.error(f"图片处理出错: {msg.msg_id}, 错误: {e}")
|
||||
return {
|
||||
'success': False,
|
||||
'message_id': msg.id,
|
||||
'message_id': msg.msg_id,
|
||||
'roomid': msg.roomid,
|
||||
'sender': msg.sender,
|
||||
'error': str(e)
|
||||
}
|
||||
|
||||
def download_file_from_url(self, url: str, target_dir: str) -> str:
|
||||
# TODO 根据获取的文件地址,从server 下载 :http://{服务ip}:2532/download/{接口返回的文件路径}
|
||||
return ""
|
||||
|
||||
def _process_image_callback(self, future):
|
||||
"""处理异步图片处理任务完成后的回调"""
|
||||
try:
|
||||
@@ -256,13 +274,13 @@ class MessageStorage:
|
||||
|
||||
# 格式化输出字符串,添加emoji和美化格式
|
||||
ranking_str = f"🏆 {yesterday} 发言排行榜 🏆\n"
|
||||
|
||||
|
||||
# 为不同名次添加不同的奖杯和样式,并发放积分
|
||||
for rank, result in enumerate(results, start=1):
|
||||
username = result['wx_id']
|
||||
speech_count = result['speech_count']
|
||||
display_name = allContacts.get(username, username)
|
||||
|
||||
|
||||
# 根据排名发放不同数量的积分
|
||||
reward_points = 0
|
||||
if rank == 1:
|
||||
@@ -280,14 +298,14 @@ class MessageStorage:
|
||||
else:
|
||||
reward_points = 5
|
||||
ranking_str += f"👍 {rank}.{display_name}: {speech_count}次 +{reward_points}积分\n"
|
||||
|
||||
|
||||
# 发放积分奖励
|
||||
if reward_points > 0:
|
||||
success, _ = self.points_db.add_points(
|
||||
username,
|
||||
groupId,
|
||||
reward_points,
|
||||
PointSource.OTHER,
|
||||
username,
|
||||
groupId,
|
||||
reward_points,
|
||||
PointSource.OTHER,
|
||||
f"{yesterday}发言排行第{rank}名奖励"
|
||||
)
|
||||
if not success:
|
||||
|
||||
Reference in New Issue
Block a user