import json import logging from typing import Any from urllib.parse import urlsplit from app.core.config import get_settings from app.core.database import SessionLocal from app.modules.feishu.constants import FEISHU_DEFAULT_OPEN_API_DOMAIN, FeishuEventSource from app.application.feishu import FeishuEventService logger = logging.getLogger(__name__) def _sdk_domain(base_url: str) -> str: parsed = urlsplit(base_url) if not parsed.scheme or not parsed.netloc: return FEISHU_DEFAULT_OPEN_API_DOMAIN return f"{parsed.scheme}://{parsed.netloc}" def _sdk_event_to_payload(event: Any) -> dict[str, Any]: try: from lark_oapi.core.json import JSON except ImportError as exc: raise RuntimeError("lark-oapi is required for Feishu long connection") from exc data = JSON.marshal(event) if not data: return {} return json.loads(data) def _handle_message_event(event: Any) -> None: payload = _sdk_event_to_payload(event) db = SessionLocal() try: result = FeishuEventService(db).handle_event( payload, source=FeishuEventSource.LONG_CONNECTION, auto_reply=True, ) logger.info("Handled Feishu long connection event: %s", result) finally: db.close() def run_long_connection() -> None: """Start the Feishu long connection client and block forever.""" settings = get_settings() if not settings.feishu_app_id or not settings.feishu_app_secret: raise RuntimeError("FEISHU_APP_ID and FEISHU_APP_SECRET are required") try: import lark_oapi as lark except ImportError as exc: raise RuntimeError("Install lark-oapi before starting Feishu long connection") from exc event_handler = ( lark.EventDispatcherHandler.builder( settings.feishu_encrypt_key or "", settings.feishu_verification_token or "", ) .register_p2_im_message_receive_v1(_handle_message_event) .build() ) client = lark.ws.Client( app_id=settings.feishu_app_id, app_secret=settings.feishu_app_secret, event_handler=event_handler, log_level=lark.LogLevel.WARNING, domain=_sdk_domain(settings.feishu_base_url), ) logger.info("Starting Feishu long connection client") client.start() if __name__ == "__main__": logging.basicConfig(level=logging.INFO) run_long_connection()