# app/main.py import asyncio import uvicorn from fastapi import FastAPI, HTTPException from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import HTMLResponse from pydantic import BaseModel # Schemas from app.models import ChatRequest, RegisterRequest, ScheduleRequest # Services from app.services.chat_service import process_chat_message from app.services.scheduler_service import ( scheduler, schedule_new_reminder, send_proactive_message_task, device_tokens, cancel_reminder, ) app = FastAPI(title="Mini-chan Tsundere API (Proactive Text Assistant)") app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) @app.on_event("startup") async def startup_event(): scheduler.start() print("Scheduler sudah hidup, siap menerima jadwal, Baka!") @app.on_event("shutdown") async def shutdown_event(): scheduler.shutdown() print("Scheduler dimatikan. Hmph.") @app.get("/", include_in_schema=False) def root(): return HTMLResponse( """

🤖 Mini-chan Tsundere API Running

Hmph… aku udah hidup di Hugging Face Spaces. Baka 😤

• Buka /docs buat Swagger UI.
• Healthcheck: GET /health
• Endpoints utama: /chat, /register-device, dan /schedule

""", status_code=200, ) @app.get("/health", tags=["Health"]) def health(): return {"status": "ok"} @app.post("/chat", tags=["Chat"]) async def handle_chat_endpoint(request: ChatRequest): response_text = await process_chat_message(request) return {"response": response_text} @app.post("/register-device", tags=["Scheduler"]) async def register_device(request: RegisterRequest): if not request.fcm_token: return {"error": "FCM token tidak boleh kosong, Baka!"} device_tokens.add(request.fcm_token) print(f"Device baru tercatat: {request.fcm_token}") return {"message": "Hmph, yaudah, devicemu aku catat. Jangan aneh-aneh."} @app.post("/schedule", tags=["Scheduler"]) async def schedule_reminder_endpoint(request: ScheduleRequest): user_id = request.user_id.strip() task = request.task.strip() if not user_id: raise HTTPException( status_code=400, detail="user_id tidak boleh kosong." ) if not task: raise HTTPException( status_code=400, detail="Task tidak boleh kosong." ) job_id = schedule_new_reminder( user_id=user_id, task=task, remind_at=request.remind_at_utc ) return { "message": ( f"Cih, yaudah aku ingetin nanti jam " f"{request.remind_at_utc.isoformat()}." ), "job_id": job_id, } @app.get("/debug/jobs", tags=["Debug"]) async def list_jobs(): jobs = [] for job in scheduler.get_jobs(): jobs.append({ "id": job.id, "next_run_time": str(job.next_run_time), "func": str(job.func_ref), }) return {"jobs": jobs} class DebugSendNow(BaseModel): user_id: str task: str @app.post("/debug/send-now", tags=["Debug"]) async def debug_send_now(body: DebugSendNow): asyncio.create_task( send_proactive_message_task( body.user_id, body.task ) ) return { "message": "Mini-chan mencoba mengirim sekarang." } @app.post("/debug/cancel/{job_id}", tags=["Debug"]) async def debug_cancel(job_id: str): ok = cancel_reminder(job_id) return {"cancelled": ok, "job_id": job_id}