from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException, Request, status from sqlalchemy.orm import Session from app.core.background.task_queue import enqueue_feishu_inbound_event from app.core.config import get_settings from app.core.constants import ActorValue from app.core.database import get_db from app.core.security import ApiPrincipal, require_api_key from app.application.feishu import FeishuCommandService, FeishuEventService from app.modules.feishu.constants import ( FeishuEventSource, FeishuEventTransport, FeishuPayloadKey, FeishuResponseKey, ) from app.modules.feishu.event_verification import FeishuWebhookVerifier from app.modules.feishu.schemas import ( FeishuCardMessage, FeishuCommandRequest, FeishuCommandResult, FeishuSendResult, FeishuTextMessage, ) from app.modules.feishu.service import FeishuService router = APIRouter() @router.post("/webhook") async def feishu_webhook( request: Request, background_tasks: BackgroundTasks, db: Session = Depends(get_db), ) -> dict: """Handle Feishu webhook challenge and text command events.""" if get_settings().feishu_event_transport != FeishuEventTransport.WEBHOOK: raise HTTPException( status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Feishu webhook transport is disabled", ) payload = FeishuWebhookVerifier().verify(await request.body(), request.headers) acceptance = FeishuEventService(db).accept_verified_event( payload, source=FeishuEventSource.WEBHOOK, auto_reply=True, ) if acceptance.event_key is not None and acceptance.should_dispatch: background_tasks.add_task( enqueue_feishu_inbound_event, acceptance.event_key, actor=ActorValue.FEISHU, ) return acceptance.response @router.post("/send-text", response_model=FeishuSendResult) def send_text( payload: FeishuTextMessage, db: Session = Depends(get_db), principal: ApiPrincipal = Depends(require_api_key), ) -> dict: result = FeishuService(db).send_text( payload.text, receive_id=payload.receive_id, receive_id_type=payload.receive_id_type, actor=principal.actor, tenant_key=payload.tenant_key, ) return { FeishuResponseKey.OK: result.get(FeishuPayloadKey.CODE) == 0, FeishuResponseKey.PROVIDER_RESPONSE: result, } @router.post("/send-card", response_model=FeishuSendResult) def send_card( payload: FeishuCardMessage, db: Session = Depends(get_db), principal: ApiPrincipal = Depends(require_api_key), ) -> dict: result = FeishuService(db).send_card( payload.card, receive_id=payload.receive_id, receive_id_type=payload.receive_id_type, actor=principal.actor, tenant_key=payload.tenant_key, ) return { FeishuResponseKey.OK: result.get(FeishuPayloadKey.CODE) == 0, FeishuResponseKey.PROVIDER_RESPONSE: result, } @router.post( "/commands/preview", response_model=FeishuCommandResult, ) def preview_command( payload: FeishuCommandRequest, db: Session = Depends(get_db), principal: ApiPrincipal = Depends(require_api_key), ) -> dict: """Preview local Feishu command routing without requiring webhook delivery.""" return FeishuCommandService(db).handle_text( payload.text, chat_id=payload.chat_id, actor=principal.actor, auto_reply=payload.auto_reply, tenant_key=payload.tenant_key, )