345 lines
11 KiB
Python
345 lines
11 KiB
Python
import asyncio
|
|
import threading
|
|
from queue import Queue
|
|
from concurrent.futures import ThreadPoolExecutor
|
|
|
|
from flask import Blueprint, render_template, jsonify, request, current_app
|
|
from .auth import login_required
|
|
from loguru import logger
|
|
|
|
# 创建联系人管理蓝图
|
|
contacts_bp = Blueprint('contacts', __name__, url_prefix='/contacts')
|
|
|
|
# 创建线程池
|
|
message_executor = ThreadPoolExecutor(max_workers=10)
|
|
message_queue = Queue()
|
|
|
|
async def process_message(func, *args, **kwargs):
|
|
"""异步处理单个消息"""
|
|
try:
|
|
return await func(*args, **kwargs)
|
|
except Exception as e:
|
|
logger.error(f"处理消息失败: {e}")
|
|
raise
|
|
|
|
def process_message_queue():
|
|
"""后台处理消息队列的函数"""
|
|
loop = asyncio.new_event_loop()
|
|
asyncio.set_event_loop(loop)
|
|
|
|
while True:
|
|
try:
|
|
task = message_queue.get()
|
|
if task is None:
|
|
break
|
|
func, args, kwargs = task
|
|
# 创建新的协程任务
|
|
future = asyncio.run_coroutine_threadsafe(
|
|
process_message(func, *args, **kwargs),
|
|
loop
|
|
)
|
|
# 不等待结果,让任务在后台运行
|
|
future.add_done_callback(lambda f: message_queue.task_done())
|
|
except Exception as e:
|
|
logger.error(f"处理消息队列任务失败: {e}")
|
|
message_queue.task_done()
|
|
|
|
# 启动消息处理线程
|
|
message_thread = threading.Thread(target=process_message_queue, daemon=True)
|
|
message_thread.start()
|
|
|
|
# 联系人管理页面
|
|
@contacts_bp.route('/')
|
|
@login_required
|
|
def contacts_management():
|
|
"""通讯录管理页面"""
|
|
return render_template('contacts_management.html')
|
|
|
|
|
|
# API路由
|
|
@contacts_bp.route('/api/all', methods=['GET'])
|
|
@login_required
|
|
def api_contacts_all():
|
|
"""获取所有联系人信息API"""
|
|
try:
|
|
server = current_app.dashboard_server
|
|
contacts = server.contact_manager.get_contacts()
|
|
return jsonify({
|
|
"success": True,
|
|
"data": {
|
|
"contacts": contacts
|
|
}
|
|
})
|
|
except Exception as e:
|
|
logger.error(f"获取所有联系人信息失败: {e}")
|
|
return jsonify({"success": False, "error": str(e)}), 500
|
|
|
|
|
|
@contacts_bp.route('/api/statistics', methods=['GET'])
|
|
@login_required
|
|
def api_contacts_statistics():
|
|
"""获取联系人统计信息API"""
|
|
try:
|
|
server = current_app.dashboard_server
|
|
# 使用新的联系人分类方法获取统计信息
|
|
total, groups, personal, public, official = server.contact_manager.get_contact_statistics()
|
|
|
|
return jsonify({
|
|
"success": True,
|
|
"data": {
|
|
"total": total,
|
|
"groups": groups,
|
|
"personal": personal,
|
|
"public": public,
|
|
"official": official
|
|
}
|
|
})
|
|
except Exception as e:
|
|
logger.error(f"获取联系人统计信息失败: {e}")
|
|
return jsonify({"success": False, "error": str(e)}), 500
|
|
|
|
|
|
@contacts_bp.route('/api/groups', methods=['GET'])
|
|
@login_required
|
|
def api_contacts_groups():
|
|
"""获取群组联系人信息API"""
|
|
try:
|
|
server = current_app.dashboard_server
|
|
group_contacts = server.contact_manager.get_group_contacts()
|
|
|
|
return jsonify({
|
|
"success": True,
|
|
"data": {
|
|
"groups": group_contacts
|
|
}
|
|
})
|
|
except Exception as e:
|
|
logger.error(f"获取群组联系人信息失败: {e}")
|
|
return jsonify({"success": False, "error": str(e)}), 500
|
|
|
|
|
|
@contacts_bp.route('/api/personal', methods=['GET'])
|
|
@login_required
|
|
def api_contacts_personal():
|
|
"""获取个人联系人信息API"""
|
|
try:
|
|
server = current_app.dashboard_server
|
|
personal_contacts = server.contact_manager.get_personal_contacts()
|
|
|
|
return jsonify({
|
|
"success": True,
|
|
"data": {
|
|
"personal": personal_contacts
|
|
}
|
|
})
|
|
except Exception as e:
|
|
logger.error(f"获取个人联系人信息失败: {e}")
|
|
return jsonify({"success": False, "error": str(e)}), 500
|
|
|
|
|
|
@contacts_bp.route('/api/official', methods=['GET'])
|
|
@login_required
|
|
def api_contacts_official():
|
|
"""获取公众号联系人信息API"""
|
|
try:
|
|
server = current_app.dashboard_server
|
|
official_accounts = server.contact_manager.get_official_accounts()
|
|
|
|
return jsonify({
|
|
"success": True,
|
|
"data": {
|
|
"official": official_accounts
|
|
}
|
|
})
|
|
except Exception as e:
|
|
logger.error(f"获取公众号联系人信息失败: {e}")
|
|
return jsonify({"success": False, "error": str(e)}), 500
|
|
|
|
|
|
@contacts_bp.route('/api/public', methods=['GET'])
|
|
@login_required
|
|
def api_contacts_public():
|
|
"""获取公共好友信息API"""
|
|
try:
|
|
server = current_app.dashboard_server
|
|
public_contacts = server.contact_manager.get_public_contacts()
|
|
|
|
return jsonify({
|
|
"success": True,
|
|
"data": {
|
|
"public": public_contacts
|
|
}
|
|
})
|
|
except Exception as e:
|
|
logger.error(f"获取公共好友信息失败: {e}")
|
|
return jsonify({"success": False, "error": str(e)}), 500
|
|
|
|
|
|
@contacts_bp.route('/api/head_images', methods=['GET'])
|
|
@login_required
|
|
def api_head_images():
|
|
"""获取联系人头像信息API"""
|
|
try:
|
|
server = current_app.dashboard_server
|
|
head_images = server.contact_manager.get_all_head_images()
|
|
|
|
return jsonify({
|
|
"success": True,
|
|
"data": {
|
|
"head_images": head_images
|
|
}
|
|
})
|
|
except Exception as e:
|
|
logger.error(f"获取联系人头像信息失败: {e}")
|
|
return jsonify({"success": False, "error": str(e)}), 500
|
|
|
|
|
|
@contacts_bp.route('/api/group_members/<roomid>', methods=['GET'])
|
|
@login_required
|
|
def api_group_members(roomid):
|
|
"""获取指定群的成员列表API
|
|
|
|
Args:
|
|
roomid: 群ID
|
|
"""
|
|
try:
|
|
server = current_app.dashboard_server
|
|
group_members = server.contact_db.get_chatroom_member_list(roomid)
|
|
return jsonify({
|
|
"success": True,
|
|
"data": {
|
|
"members": group_members
|
|
}
|
|
})
|
|
except Exception as e:
|
|
logger.error(f"获取群成员列表失败: {e}")
|
|
return jsonify({"success": False, "error": str(e)}), 500
|
|
|
|
|
|
@contacts_bp.route('/api/update', methods=['POST'])
|
|
@login_required
|
|
def api_contacts_update():
|
|
"""更新通讯录信息API"""
|
|
try:
|
|
server = current_app.dashboard_server
|
|
# 假设 contact_manager 有 update_contacts 方法用于同步通讯录
|
|
result = asyncio.run(server.robot.refresh_contacts_db())
|
|
if result:
|
|
return jsonify({"success": True, "message": "通讯录更新成功"})
|
|
else:
|
|
return jsonify({"success": False, "message": "通讯录更新失败"}), 500
|
|
except Exception as e:
|
|
logger.error(f"更新通讯录失败: {e}")
|
|
return jsonify({"success": False, "message": f"更新通讯录失败: {str(e)}"}), 500
|
|
|
|
|
|
@contacts_bp.route('/api/send_message', methods=['POST'])
|
|
@login_required
|
|
def api_send_message():
|
|
"""发送消息API
|
|
|
|
支持的消息类型:
|
|
- text: 文本消息
|
|
- image: 图片消息
|
|
- voice: 语音消息
|
|
- video: 视频消息
|
|
- link: 链接消息
|
|
"""
|
|
try:
|
|
data = request.form if request.files else request.json
|
|
wxid = data.get('wxid')
|
|
msg_type = data.get('type')
|
|
content = data.get('content')
|
|
|
|
if not wxid or not msg_type:
|
|
return jsonify({'success': False, 'message': '缺少必要参数'})
|
|
|
|
# 获取机器人实例
|
|
server = current_app.dashboard_server
|
|
if not server or not server.client:
|
|
return jsonify({'success': False, 'message': '机器人未初始化'})
|
|
|
|
# 根据消息类型将任务加入队列
|
|
if msg_type == 'text':
|
|
message_queue.put((
|
|
server.client.send_text_message,
|
|
(wxid, content),
|
|
{}
|
|
))
|
|
return jsonify({
|
|
'success': True,
|
|
'message': '消息已加入发送队列'
|
|
})
|
|
|
|
elif msg_type == 'image':
|
|
if 'file' not in request.files:
|
|
return jsonify({'success': False, 'message': '未上传文件'})
|
|
file = request.files['file']
|
|
message_queue.put((
|
|
server.client.send_image_message,
|
|
(wxid, file.read()),
|
|
{}
|
|
))
|
|
return jsonify({
|
|
'success': True,
|
|
'message': '消息已加入发送队列'
|
|
})
|
|
|
|
elif msg_type == 'voice':
|
|
if 'file' not in request.files:
|
|
return jsonify({'success': False, 'message': '未上传文件'})
|
|
file = request.files['file']
|
|
if file.filename.endswith('.mp3'):
|
|
format_str = "mp3"
|
|
elif file.filename.endswith('.wav'):
|
|
format_str = "wav"
|
|
else:
|
|
return jsonify({
|
|
'success': False,
|
|
'message': '不支持的音频格式'
|
|
})
|
|
message_queue.put((
|
|
server.client.send_voice_message,
|
|
(wxid, file.read()),
|
|
{'format': format_str}
|
|
))
|
|
return jsonify({
|
|
'success': True,
|
|
'message': '消息已加入发送队列'
|
|
})
|
|
|
|
elif msg_type == 'video':
|
|
if 'file' not in request.files:
|
|
return jsonify({'success': False, 'message': '未上传文件'})
|
|
file = request.files['file']
|
|
message_queue.put((
|
|
server.client.send_video_message,
|
|
(wxid, file.read()),
|
|
{}
|
|
))
|
|
return jsonify({
|
|
'success': True,
|
|
'message': '消息已加入发送队列'
|
|
})
|
|
|
|
elif msg_type == 'link':
|
|
url = content.get('url')
|
|
title = content.get('title', '')
|
|
description = content.get('description', '')
|
|
message_queue.put((
|
|
server.client.send_link_message,
|
|
(wxid, url, title, description),
|
|
{}
|
|
))
|
|
return jsonify({
|
|
'success': True,
|
|
'message': '消息已加入发送队列'
|
|
})
|
|
|
|
else:
|
|
return jsonify({'success': False, 'message': '不支持的消息类型'})
|
|
|
|
except Exception as e:
|
|
logger.exception(f"发送消息失败: {e}")
|
|
return jsonify({'success': False, 'message': str(e)}), 500
|