Spaces:
Running
Running
| # 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=["*"], | |
| ) | |
| async def startup_event(): | |
| scheduler.start() | |
| print("Scheduler sudah hidup, siap menerima jadwal, Baka!") | |
| async def shutdown_event(): | |
| scheduler.shutdown() | |
| print("Scheduler dimatikan. Hmph.") | |
| def root(): | |
| return HTMLResponse( | |
| """ | |
| <style> | |
| body{font-family:ui-sans-serif,system-ui;max-width:720px;margin:48px auto;padding:0 16px} | |
| a{color:#6b46c1;text-decoration:none} | |
| .badge{display:inline-block;background:#ede9fe;color:#5b21b6;padding:4px 8px;border-radius:999px;font-size:12px;margin-left:8px} | |
| code{background:#111827;color:#e5e7eb;padding:2px 6px;border-radius:6px} | |
| </style> | |
| <h2>🤖 Mini-chan Tsundere API <span class="badge">Running</span></h2> | |
| <p>Hmph… aku udah hidup di Hugging Face Spaces. Baka 😤</p> | |
| <p>• Buka <a href="/docs">/docs</a> buat Swagger UI.<br/> | |
| • Healthcheck: <code>GET /health</code><br/> | |
| • Endpoints utama: <code>/chat</code>, <code>/register-device</code>, dan <code>/schedule</code></p> | |
| """, | |
| status_code=200, | |
| ) | |
| def health(): | |
| return {"status": "ok"} | |
| async def handle_chat_endpoint(request: ChatRequest): | |
| response_text = await process_chat_message(request) | |
| return {"response": response_text} | |
| 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."} | |
| 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, | |
| } | |
| 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 | |
| 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." | |
| } | |
| async def debug_cancel(job_id: str): | |
| ok = cancel_reminder(job_id) | |
| return {"cancelled": ok, "job_id": job_id} |