from typing import Any from app.core.constants import ActorValue from app.core.database import SessionLocal from app.modules.ai_agent.constants import AIResponseKey from app.modules.ai_agent.service import AIService from app.modules.feishu.service import FeishuService from app.modules.subscriptions.services import ( DeliveryGenerationRequest, DeliverySendRequest, DeliveryService, RetryableDeliveryError, SubscriptionScanner, ) class AISubscriptionGenerator: """Generate side-effect-free subscription content through the configured AI.""" def __init__(self, ai: AIService): self.ai = ai def generate(self, request: DeliveryGenerationRequest) -> str: result = self.ai.generate_scheduled( request.prompt, owner_id=request.owner_id, group=request.use_company_rules and not request.use_personal_context, actor=ActorValue.SCHEDULER, ) if not result.get(AIResponseKey.OK): raise RetryableDeliveryError("AI provider is unavailable") return str(result[AIResponseKey.ANSWER]) class FeishuSubscriptionSender: """Send a delivery with the stable Feishu UUID supplied by durable state.""" def __init__(self, feishu: FeishuService): self.feishu = feishu def send(self, request: DeliverySendRequest) -> dict[str, Any]: return self.feishu.send_text( request.text, receive_id=request.receive_id, receive_id_type=request.receive_id_type, actor=ActorValue.SCHEDULER, uuid=request.uuid, tenant_key=request.tenant_key, ) def run_subscription_cycle( *, actor: str = ActorValue.SCHEDULER, ) -> dict[str, Any]: """Materialize due windows and process pending/retry deliveries.""" _ = actor db = SessionLocal() try: created = SubscriptionScanner(db).scan_due() delivery_service = DeliveryService( db, generator=AISubscriptionGenerator(AIService(db)), sender=FeishuSubscriptionSender(FeishuService(db)), ) processed = delivery_service.process_due() return { "created": [item.code for item in created], "processed": [ {"code": item.code, "status": item.status} for item in processed ], } finally: db.close()