You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
221 lines
7.2 KiB
221 lines
7.2 KiB
import uuid
|
|
import httpx
|
|
from fastapi import APIRouter, Depends, HTTPException, Request
|
|
from sqlalchemy import select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
from database import get_db
|
|
from models import User, ChatSession, ChatMessage
|
|
|
|
router = APIRouter(prefix="/api/wecom", tags=["wecom"])
|
|
|
|
|
|
@router.post("/callback")
|
|
async def wecom_callback(request: Request, db: AsyncSession = Depends(get_db)):
|
|
"""
|
|
接收企业微信回调消息,路由到AI助手处理并回复。
|
|
企微配置的回调URL指向此端点。
|
|
"""
|
|
try:
|
|
body = await request.json()
|
|
except Exception:
|
|
body = await request.body()
|
|
|
|
msg_type = "text"
|
|
wecom_user_id = ""
|
|
content = ""
|
|
if isinstance(body, dict):
|
|
msg_type = body.get("msg_type", body.get("MsgType", "text"))
|
|
wecom_user_id = body.get("user_id", body.get("FromUserName", ""))
|
|
content = body.get("content", body.get("Content", ""))
|
|
|
|
if not wecom_user_id or not content:
|
|
return {"code": 200, "message": "received"}
|
|
|
|
user_result = await db.execute(
|
|
select(User).where(User.wecom_user_id == wecom_user_id)
|
|
)
|
|
user = user_result.scalar_one_or_none()
|
|
if not user:
|
|
return {"code": 200, "message": "received", "data": {"note": "user not found"}}
|
|
|
|
from agentscope.message import Msg
|
|
|
|
session_result = await db.execute(
|
|
select(ChatSession)
|
|
.where(ChatSession.user_id == user.id, ChatSession.agent_type == "employee")
|
|
.order_by(ChatSession.updated_at.desc())
|
|
.limit(1)
|
|
)
|
|
session = session_result.scalar_one_or_none()
|
|
session_id = f"wecom_{wecom_user_id}_{uuid.uuid4().hex[:8]}"
|
|
if not session:
|
|
session = ChatSession(
|
|
user_id=user.id, agent_type="employee",
|
|
session_id=session_id,
|
|
)
|
|
db.add(session)
|
|
await db.flush()
|
|
|
|
user_msg = ChatMessage(
|
|
session_id=session.id, user_id=user.id,
|
|
role="user", content=content,
|
|
)
|
|
db.add(user_msg)
|
|
await db.flush()
|
|
|
|
from agentscope_integration.factory import AgentFactory
|
|
agent = await AgentFactory.create_agent(
|
|
agent_type="employee",
|
|
user_id=str(user.id),
|
|
user_name=user.display_name,
|
|
department_id=str(user.department_id) if user.department_id else None,
|
|
)
|
|
|
|
input_msg = Msg(name="user", content=content, role="user")
|
|
response = await agent.reply(input_msg)
|
|
|
|
reply_text = response.get_text_content() if hasattr(response, 'get_text_content') else str(response)
|
|
|
|
ai_msg = ChatMessage(
|
|
session_id=session.id, user_id=user.id,
|
|
role="assistant", content=reply_text,
|
|
)
|
|
db.add(ai_msg)
|
|
|
|
return {
|
|
"code": 200,
|
|
"message": "ok",
|
|
"data": {
|
|
"msg_type": msg_type,
|
|
"user_id": wecom_user_id,
|
|
"reply": reply_text,
|
|
},
|
|
}
|
|
|
|
|
|
@router.post("/send")
|
|
async def send_wecom_message(
|
|
request: Request,
|
|
payload: dict,
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
"""
|
|
向企业微信用户推送消息。
|
|
生产环境中需配置真实的企微API凭据。
|
|
"""
|
|
to_user = payload.get("to_user", "")
|
|
msg_content = payload.get("content", "")
|
|
msg_type = payload.get("msg_type", "text")
|
|
|
|
if not to_user:
|
|
raise HTTPException(400, "缺少目标用户")
|
|
|
|
corp_id = ""
|
|
corp_secret = ""
|
|
wecom_message_id = f"msg_{uuid.uuid4().hex[:12]}"
|
|
|
|
if corp_id and corp_secret:
|
|
try:
|
|
async with httpx.AsyncClient() as client:
|
|
token_resp = await client.get(
|
|
"https://qyapi.weixin.qq.com/cgi-bin/gettoken",
|
|
params={"corpid": corp_id, "corpsecret": corp_secret},
|
|
)
|
|
token_data = token_resp.json()
|
|
access_token = token_data.get("access_token")
|
|
|
|
if access_token:
|
|
msg_body = {
|
|
"touser": to_user,
|
|
"msgtype": msg_type,
|
|
"agentid": 0,
|
|
}
|
|
if msg_type == "text":
|
|
msg_body["text"] = {"content": msg_content}
|
|
elif msg_type == "textcard":
|
|
msg_body["textcard"] = payload.get("card", {})
|
|
|
|
msg_resp = await client.post(
|
|
f"https://qyapi.weixin.qq.com/cgi-bin/message/send",
|
|
params={"access_token": access_token},
|
|
json=msg_body,
|
|
)
|
|
resp_data = msg_resp.json()
|
|
if resp_data.get("errcode") == 0:
|
|
wecom_message_id = resp_data.get("msgid", wecom_message_id)
|
|
except Exception:
|
|
pass
|
|
|
|
return {
|
|
"code": 200,
|
|
"message": "消息已发送",
|
|
"data": {"wecom_message_id": wecom_message_id},
|
|
}
|
|
|
|
|
|
@router.get("/config")
|
|
async def get_wecom_config(request: Request):
|
|
return {
|
|
"code": 200,
|
|
"data": {
|
|
"bot_name": "企业AI助手",
|
|
"status": "active" if settings.WECOM_CORP_ID else "unconfigured",
|
|
"corp_id": settings.WECOM_CORP_ID or "",
|
|
"agent_id": getattr(settings, 'WECOM_AGENT_ID', 0),
|
|
"features": ["消息对话", "文件处理", "任务通知", "工作流触发"],
|
|
},
|
|
}
|
|
|
|
|
|
@router.put("/config")
|
|
async def update_wecom_config(request: Request, payload: dict):
|
|
import os
|
|
env_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), '.env')
|
|
updates = {}
|
|
if "corp_id" in payload:
|
|
updates["WECOM_CORP_ID"] = payload["corp_id"]
|
|
if "secret" in payload:
|
|
updates["WECOM_APP_SECRET"] = payload["secret"]
|
|
if "agent_id" in payload:
|
|
updates["WECOM_AGENT_ID"] = str(payload["agent_id"])
|
|
if "token" in payload:
|
|
updates["WECOM_TOKEN"] = payload["token"]
|
|
if "encoding_aes_key" in payload:
|
|
updates["WECOM_ENCODING_AES_KEY"] = payload["encoding_aes_key"]
|
|
|
|
if updates:
|
|
lines = []
|
|
if os.path.exists(env_path):
|
|
with open(env_path, 'r') as f:
|
|
lines = f.readlines()
|
|
existing_keys = {l.split('=')[0].strip() for l in lines if '=' in l and not l.startswith('#')}
|
|
for key, value in updates.items():
|
|
if key in existing_keys:
|
|
lines = [f"{key}={value}\n" if l.split('=')[0].strip() == key else l for l in lines]
|
|
else:
|
|
lines.append(f"{key}={value}\n")
|
|
with open(env_path, 'w') as f:
|
|
f.writelines(lines)
|
|
|
|
for key, value in updates.items():
|
|
if hasattr(settings, key):
|
|
setattr(settings, key, value)
|
|
|
|
return {"code": 200, "message": "配置已保存"}
|
|
|
|
|
|
@router.post("/send")
|
|
async def send_wecom_message(request: Request, payload: dict):
|
|
to_user = payload.get("to_user", "@all")
|
|
msg_type = payload.get("msg_type", "text")
|
|
content = payload.get("content", "")
|
|
|
|
if not content:
|
|
return {"code": 400, "message": "消息内容不能为空"}
|
|
|
|
try:
|
|
from agentscope_integration.tools.wecom_tools import send_notification
|
|
result = send_notification(to_user, content, msg_type)
|
|
return {"code": 200, "message": result}
|
|
except Exception as e:
|
|
return {"code": 500, "message": f"发送失败: {e}"}
|