433 lines
14 KiB
Python
433 lines
14 KiB
Python
#!/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🛑 服务已停止")
|