Files
PlantAI-dev/wechat_task_bridge.py
2026-08-03 18:42:47 +08:00

433 lines
14 KiB
Python
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
WeChat Task Bridge - 微信与 bbav20311 任务系统的桥接
功能:
1. 用户从微信发送分析请求
2. 自动创建并执行任务
3. 任务完成后微信通知用户
使用:
python wechat_task_bridge.py
作者: xialab的幸运星
日期: 2026-04-10
"""
import asyncio
import json
import re
import sys
from pathlib import Path
from typing import Dict, Optional
from datetime import datetime
# 添加项目路径
PROJECT_ROOT = Path(__file__).parent
sys.path.insert(0, str(PROJECT_ROOT))
from wechat_integration import WeChatIntegration, WeChatTaskNotifier
# 导入 bbav20311 的任务模块
try:
from apps.web.pipeline_backend import new_job, run_stage
from gfa_core.agents.router import route, is_analysis_task
BACKEND_AVAILABLE = True
except ImportError as e:
print(f"⚠️ 无法导入后端模块: {e}")
print(" 将以模拟模式运行")
BACKEND_AVAILABLE = False
class WeChatTaskBridge:
"""
微信任务桥接器
将微信消息转换为 bbav20311 分析任务
"""
def __init__(self):
self.wechat = WeChatIntegration()
self.active_tasks: Dict[str, dict] = {} # 存储活跃任务
self.user_sessions: Dict[str, dict] = {} # 用户会话状态
async def start(self):
"""启动桥接服务"""
print("🚀 启动 WeChat Task Bridge")
print("="*60)
# 1. 登录微信
print("\n🔐 正在登录微信...")
success = await self.wechat.login(timeout=300)
if not success:
print("❌ 微信登录失败,服务无法启动")
return
print("✅ 微信登录成功")
# 2. 启动消息监听
print("\n🎧 启动消息监听...")
print(" 用户现在可以通过微信发送分析请求")
print(" 按 Ctrl+C 停止服务\n")
await self.wechat.start_message_listener(
callback=self._handle_message,
poll_interval=5
)
async def _handle_message(self, msg: dict):
"""处理收到的微信消息"""
user_id = msg.get("from")
content = msg.get("content", "").strip()
print(f"\n📨 收到消息 [{user_id}]: {content}")
# 显示正在输入状态
await self.wechat.send_typing(user_id)
# 解析命令
response = await self._parse_command(user_id, content)
# 发送回复
if response:
await self.wechat.send_text_message(user_id, response)
async def _parse_command(self, user_id: str, content: str) -> str:
"""解析用户命令并返回回复"""
content_lower = content.lower()
# 1. 帮助命令
if content in ["帮助", "help", "?", "菜单"]:
return self._get_help_message()
# 2. 状态查询
if content in ["状态", "status", "系统状态"]:
return await self._get_system_status()
# 3. 任务列表
if content in ["任务", "任务列表", "tasks"]:
return self._get_task_list(user_id)
# 4. 任务详情
detail_match = re.match(r"详情\s+(\w+)", content)
if detail_match:
task_id = detail_match.group(1)
return self._get_task_detail(task_id)
# 5. 取消任务
cancel_match = re.match(r"取消\s+(\w+)", content)
if cancel_match:
task_id = cancel_match.group(1)
return await self._cancel_task(user_id, task_id)
# 6. 分析请求 - 尝试解析为基因家族分析任务
if BACKEND_AVAILABLE:
analysis_response = await self._handle_analysis_request(user_id, content)
if analysis_response:
return analysis_response
# 7. 未知命令
return f"""❓ 无法理解的命令: "{content}"
请使用以下格式:
• 帮助 - 显示帮助信息
• 状态 - 查询系统状态
• 任务列表 - 查看任务
• 详情 <任务ID> - 查看详情
• 或直接发送分析需求,如:"Ft.SPL 基因家族鉴定"
发送"帮助"查看完整命令列表。"""
def _get_help_message(self) -> str:
"""获取帮助信息"""
return """🤖 bbav20311 基因家族分析助手
📋 可用命令:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━
• 帮助 / help - 显示此帮助
• 状态 / status - 查询系统状态
• 任务 / 任务列表 - 查看进行中的任务
• 详情 <任务ID> - 查看任务详情
• 取消 <任务ID> - 取消任务
🧬 分析任务:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━
直接发送分析需求,例如:
• "Ft.SPL 基因家族鉴定"
• "分析 Ft.WRKY 的进化树"
• "Ft.MYB motif分析"
📊 支持的 stage:
• identify - 基因家族鉴定
• phylogeny - 进化树构建
• motif - Motif分析
• domain - 结构域分析
• promoter - 启动子分析
• synteny - 共线性分析
• expression - 表达分析
• combo - 综合分析
💡 提示:
• 任务完成后会自动通知您
• 大任务可能需要几分钟到几小时
• 可随时查询任务状态"""
async def _get_system_status(self) -> str:
"""获取系统状态"""
if not BACKEND_AVAILABLE:
return "⚠️ 后端服务未连接(模拟模式)"
# 检查后端服务状态
active_count = len(self.active_tasks)
return f"""📊 系统状态
✅ 微信服务: 在线
✅ 后端服务: 运行中
📋 活跃任务: {active_count} 个
系统运行正常,可以接收分析请求。"""
def _get_task_list(self, user_id: str) -> str:
"""获取用户的任务列表"""
user_tasks = [
tid for tid, task in self.active_tasks.items()
if task.get("user_id") == user_id
]
if not user_tasks:
return "📭 您当前没有进行中的任务"
lines = [f"📋 您的任务列表 ({len(user_tasks)}个):"]
for tid in user_tasks[-5:]: # 只显示最近5个
task = self.active_tasks[tid]
status_emoji = {
"pending": "⏳",
"running": "🔄",
"completed": "✅",
"failed": "❌"
}.get(task.get("status"), "❓")
lines.append(f"{status_emoji} {tid} - {task.get('task_type', '未知')}")
lines.append("\n发送'详情 <任务ID>'查看详细信息")
return "\n".join(lines)
def _get_task_detail(self, task_id: str) -> str:
"""获取任务详情"""
task = self.active_tasks.get(task_id)
if not task:
return f"❌ 未找到任务: {task_id}"
status_emoji = {
"pending": "⏳",
"running": "🔄",
"completed": "✅",
"failed": "❌"
}.get(task.get("status"), "❓")
lines = [
f"📋 任务详情: {task_id}",
f"状态: {status_emoji} {task.get('status', '未知')}",
f"类型: {task.get('task_type', '未知')}",
f"创建时间: {task.get('created_at', '未知')}",
]
if task.get("started_at"):
lines.append(f"开始时间: {task['started_at']}")
if task.get("completed_at"):
lines.append(f"完成时间: {task['completed_at']}")
if task.get("error"):
lines.append(f"错误信息: {task['error']}")
if task.get("output_files"):
lines.append(f"\n输出文件:")
for f in task["output_files"][:5]:
lines.append(f" • {f}")
return "\n".join(lines)
async def _cancel_task(self, user_id: str, task_id: str) -> str:
"""取消任务"""
task = self.active_tasks.get(task_id)
if not task:
return f"❌ 未找到任务: {task_id}"
if task.get("user_id") != user_id:
return "❌ 您无权取消此任务"
if task.get("status") in ["completed", "failed"]:
return "❌ 任务已完成或已失败,无法取消"
# 取消任务逻辑
task["status"] = "cancelled"
return f"✅ 任务 {task_id} 已取消"
async def _handle_analysis_request(self, user_id: str, content: str) -> Optional[str]:
"""
处理分析请求
使用 bbav20311 的意图识别和任务系统
"""
try:
# 使用 router 进行意图识别
intent = route(content)
if not is_analysis_task(intent):
return None # 不是分析任务
# 提取参数
prefix = intent.prefix
stage = intent.task_type if intent.task_type != "pipeline" else "full"
if not prefix:
return "❌ 无法识别基因家族前缀,请使用格式如:Ft.SPL 基因家族"
# 创建任务
task_id = f"wx_{datetime.now().strftime('%Y%m%d_%H%M%S')}_{user_id[:8]}"
task_info = {
"task_id": task_id,
"user_id": user_id,
"task_type": stage,
"prefix": prefix,
"status": "pending",
"created_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"parameters": intent.parameters
}
self.active_tasks[task_id] = task_info
# 启动异步任务执行
asyncio.create_task(self._execute_task(task_info))
return f"""✅ 分析任务已创建
📋 任务ID: {task_id}
🧬 基因家族: {prefix}
🔬 分析类型: {stage}
⏳ 状态: 排队中
任务完成后将通过微信通知您。
发送"详情 {task_id}"查看进度。"""
except Exception as e:
print(f"处理分析请求失败: {e}")
return None
async def _execute_task(self, task_info: dict):
"""执行任务"""
task_id = task_info["task_id"]
user_id = task_info["user_id"]
# 创建通知器
notifier = WeChatTaskNotifier(user_wechat_id=user_id, wechat=self.wechat)
try:
# 更新状态
task_info["status"] = "running"
task_info["started_at"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
# 通知开始
await notifier.notify_task_started(
task_id=task_id,
task_type=task_info["task_type"]
)
if BACKEND_AVAILABLE:
# 创建 bbav20311 job
job = new_job()
# 运行任务
# 这里需要根据 task_type 调用相应的 stage
stage = task_info["task_type"]
prefix = task_info["prefix"]
print(f"执行任务: {task_id}, stage={stage}, prefix={prefix}")
# 模拟任务执行(实际实现需要调用 pipeline_backend)
await asyncio.sleep(5) # 模拟执行时间
# 更新任务信息
task_info["status"] = "completed"
task_info["completed_at"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
task_info["output_files"] = [
f"{prefix}.{stage}.result.txt",
f"{prefix}.{stage}.report.pdf"
]
# 通知完成
await notifier.notify_task_completed(
task_id=task_id,
task_type=task_info["task_type"],
output_files=task_info["output_files"]
)
else:
# 模拟模式
await asyncio.sleep(3)
task_info["status"] = "completed"
task_info["completed_at"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
task_info["output_files"] = ["simulated_result.txt"]
await notifier.notify_task_completed(
task_id=task_id,
task_type=task_info["task_type"],
output_files=task_info["output_files"]
)
except Exception as e:
task_info["status"] = "failed"
task_info["error"] = str(e)
task_info["completed_at"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
await notifier.notify_task_failed(
task_id=task_id,
task_type=task_info["task_type"],
error=str(e)
)
# ==================== 测试 ====================
async def test_bridge():
"""测试桥接器"""
print("="*60)
print("🧪 WeChat Task Bridge 测试")
print("="*60)
bridge = WeChatTaskBridge()
# 模拟消息处理
test_cases = [
("user_001", "帮助"),
("user_001", "状态"),
("user_001", "Ft.SPL 基因家族鉴定"),
("user_001", "任务列表"),
]
for user_id, content in test_cases:
print(f"\n[Test] 用户: {user_id}, 消息: {content}")
response = await bridge._parse_command(user_id, content)
print(f"[Response] {response[:200]}...")
await asyncio.sleep(1)
print("\n" + "="*60)
print("✅ 测试完成")
print("="*60)
if __name__ == "__main__":
import sys
if len(sys.argv) > 1 and sys.argv[1] == "test":
# 运行测试模式
asyncio.run(test_bridge())
else:
# 运行服务
bridge = WeChatTaskBridge()
try:
asyncio.run(bridge.start())
except KeyboardInterrupt:
print("\n\n🛑 服务已停止")