209 lines
6.9 KiB
Python
209 lines
6.9 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
企业微信会话存档API路由
|
||
"""
|
||
|
||
from typing import Optional
|
||
from fastapi import APIRouter, HTTPException, Query, Depends, BackgroundTasks
|
||
from fastapi.responses import FileResponse, JSONResponse
|
||
import logging
|
||
|
||
from app.services.wework_service import get_wework_service, WeworkService
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
router = APIRouter(
|
||
prefix="/wework",
|
||
tags=["wework"],
|
||
responses={404: {"description": "Not found"}},
|
||
)
|
||
|
||
def get_service():
|
||
"""依赖注入:获取企业微信服务实例"""
|
||
return get_wework_service()
|
||
|
||
@router.get("/health")
|
||
async def health_check(service: WeworkService = Depends(get_service)):
|
||
"""健康检查端点"""
|
||
return {
|
||
"status": "healthy",
|
||
"service": "wework",
|
||
"corpid": service.corpid,
|
||
"media_dir": str(service.media_dir)
|
||
}
|
||
|
||
@router.get("/chat")
|
||
async def get_chat(
|
||
seq: int = Query(0, description="从指定的seq开始拉取消息,首次使用请使用0"),
|
||
limit: int = Query(100, description="一次拉取的消息条数,最大值1000条", ge=1, le=1000),
|
||
proxy: Optional[str] = Query(None, description="代理链接,如:socks5://10.0.0.1:8081 或 http://10.0.0.1:8081"),
|
||
passwd: Optional[str] = Query(None, description="代理账号密码,如:user_name:passwd_123"),
|
||
timeout: int = Query(5, description="超时时间,单位秒", ge=1, le=60),
|
||
service: WeworkService = Depends(get_service)
|
||
):
|
||
"""
|
||
拉取会话存档
|
||
|
||
从企业微信拉取聊天记录,支持代理和分页
|
||
"""
|
||
try:
|
||
result = service.get_chat_data(
|
||
seq=seq,
|
||
limit=limit,
|
||
proxy=proxy or "",
|
||
passwd=passwd or "",
|
||
timeout=timeout
|
||
)
|
||
return result
|
||
except Exception as e:
|
||
logger.error(f"拉取会话存档失败: {e}")
|
||
raise HTTPException(status_code=500, detail=f"拉取会话存档失败: {str(e)}")
|
||
|
||
@router.post("/media")
|
||
async def download_media(
|
||
fileid: str = Query(..., description="媒体文件id,从解密后的会话存档中得到"),
|
||
filename: Optional[str] = Query(None, description="保存的文件名,如果为空则自动生成"),
|
||
proxy: Optional[str] = Query(None, description="代理链接"),
|
||
passwd: Optional[str] = Query(None, description="代理账号密码"),
|
||
timeout: int = Query(5, description="超时时间,单位秒", ge=1, le=60),
|
||
background_tasks: BackgroundTasks = None,
|
||
service: WeworkService = Depends(get_service)
|
||
):
|
||
"""
|
||
拉取媒体文件
|
||
|
||
下载企业微信中的媒体文件(图片、语音、视频等),支持大文件分片下载
|
||
"""
|
||
try:
|
||
result = service.get_media_data(
|
||
sdk_fileid=fileid,
|
||
filename=filename,
|
||
proxy=proxy or "",
|
||
passwd=passwd or "",
|
||
timeout=timeout
|
||
)
|
||
|
||
if result.get("success"):
|
||
# 可以添加后台任务,比如发送下载完成通知等
|
||
if background_tasks:
|
||
background_tasks.add_task(
|
||
logger.info,
|
||
f"媒体文件下载完成: {result['filename']}, 大小: {result['filesize']} 字节"
|
||
)
|
||
|
||
return result
|
||
else:
|
||
raise HTTPException(status_code=500, detail=result.get("error", "下载失败"))
|
||
|
||
except Exception as e:
|
||
logger.error(f"拉取媒体文件失败: {e}")
|
||
raise HTTPException(status_code=500, detail=f"拉取媒体文件失败: {str(e)}")
|
||
|
||
@router.get("/media/files")
|
||
async def list_media_files(
|
||
service: WeworkService = Depends(get_service)
|
||
):
|
||
"""
|
||
获取已下载的媒体文件列表
|
||
|
||
返回所有已下载的媒体文件信息
|
||
"""
|
||
try:
|
||
files = service.get_media_files()
|
||
return {
|
||
"count": len(files),
|
||
"files": files
|
||
}
|
||
except Exception as e:
|
||
logger.error(f"获取媒体文件列表失败: {e}")
|
||
raise HTTPException(status_code=500, detail=f"获取媒体文件列表失败: {str(e)}")
|
||
|
||
@router.get("/media/download/{filename}")
|
||
async def download_file(
|
||
filename: str,
|
||
service: WeworkService = Depends(get_service)
|
||
):
|
||
"""
|
||
下载媒体文件
|
||
|
||
通过文件名下载已保存的媒体文件
|
||
"""
|
||
try:
|
||
file_path = service.media_dir / filename
|
||
if not file_path.exists():
|
||
raise HTTPException(status_code=404, detail="文件不存在")
|
||
|
||
return FileResponse(
|
||
path=file_path,
|
||
filename=filename,
|
||
media_type='application/octet-stream'
|
||
)
|
||
except HTTPException:
|
||
raise
|
||
except Exception as e:
|
||
logger.error(f"下载文件失败: {e}")
|
||
raise HTTPException(status_code=500, detail=f"下载文件失败: {str(e)}")
|
||
|
||
@router.delete("/media/cleanup")
|
||
async def cleanup_media_files(
|
||
days: int = Query(7, description="保留天数,早于此天数的文件将被删除", ge=1, le=365),
|
||
service: WeworkService = Depends(get_service)
|
||
):
|
||
"""
|
||
清理旧的媒体文件
|
||
|
||
删除指定天数之前的媒体文件
|
||
"""
|
||
try:
|
||
deleted_count = service.cleanup_old_files(days)
|
||
return {
|
||
"message": f"成功删除 {deleted_count} 个旧文件",
|
||
"deleted_count": deleted_count,
|
||
"days": days
|
||
}
|
||
except Exception as e:
|
||
logger.error(f"清理媒体文件失败: {e}")
|
||
raise HTTPException(status_code=500, detail=f"清理媒体文件失败: {str(e)}")
|
||
|
||
@router.post("/decrypt")
|
||
async def decrypt_message(
|
||
encrypt_key: str = Query(..., description="解密密钥,使用RSA私钥解密encrypt_random_key后得到"),
|
||
encrypt_msg: str = Query(..., description="加密的消息内容"),
|
||
service: WeworkService = Depends(get_service)
|
||
):
|
||
"""
|
||
解密会话内容
|
||
|
||
使用企业自持的RSA私钥解密后的encrypt_key来解密encrypt_chat_msg
|
||
"""
|
||
try:
|
||
result = service.decrypt_data(encrypt_key, encrypt_msg)
|
||
return result
|
||
except Exception as e:
|
||
logger.error(f"解密消息失败: {e}")
|
||
raise HTTPException(status_code=500, detail=f"解密消息失败: {str(e)}")
|
||
|
||
@router.get("/config")
|
||
async def get_config(
|
||
service: WeworkService = Depends(get_service)
|
||
):
|
||
"""
|
||
获取当前配置
|
||
|
||
返回企业微信服务的配置信息
|
||
"""
|
||
return {
|
||
"corpid": service.corpid,
|
||
"corpid_masked": service.corpid[:8] + "..." + service.corpid[-4:] if len(service.corpid) > 12 else service.corpid,
|
||
"media_dir": str(service.media_dir),
|
||
"media_dir_exists": service.media_dir.exists()
|
||
}
|
||
|
||
# 应用关闭时的清理
|
||
@router.on_event("shutdown")
|
||
async def shutdown_event():
|
||
"""应用关闭时清理资源"""
|
||
from app.services.wework_service import close_wework_service
|
||
close_wework_service()
|
||
logger.info("企业微信服务已关闭")
|