diff --git a/nanoclaude/requirements.txt b/nanoclaude/requirements.txt index 57d1f0c..7fae30d 100644 --- a/nanoclaude/requirements.txt +++ b/nanoclaude/requirements.txt @@ -5,3 +5,4 @@ pydantic-settings==2.5.0 aiosqlite==0.20.0 python-multipart==0.0.9 caldav>=1.3.0 +vobject>=0.9.8 diff --git a/nanoclaude/tools/calendar_tool.py b/nanoclaude/tools/calendar_tool.py index 33f51dc..8ae9fbb 100644 --- a/nanoclaude/tools/calendar_tool.py +++ b/nanoclaude/tools/calendar_tool.py @@ -31,8 +31,9 @@ def _get_client(): def _parse_event(event) -> dict: - """VEVENT → dict.""" + """VEVENT → dict. vobject 있으면 사용, 없으면 icalendar fallback.""" try: + # vobject 방식 vevent = event.vobject_instance.vevent summary = str(vevent.summary.value) if hasattr(vevent, "summary") else "(제목 없음)" dtstart = vevent.dtstart.value @@ -43,7 +44,24 @@ def _parse_event(event) -> dict: return {"summary": summary, "start": start_str, "end": end_str} except Exception: - return {"summary": "(파싱 실패)", "start": "", "end": ""} + pass + + # fallback: icalendar 라이브러리로 raw data 파싱 + try: + from icalendar import Calendar + cal = Calendar.from_ical(event.data) + for comp in cal.walk(): + if comp.name == "VEVENT": + summary = str(comp.get("summary", "(제목 없음)")) + dtstart = comp.get("dtstart") + dtend = comp.get("dtend") + start_str = dtstart.dt.strftime("%Y-%m-%d %H:%M") if dtstart else "" + end_str = dtend.dt.strftime("%Y-%m-%d %H:%M") if dtend else "" + return {"summary": summary, "start": start_str, "end": end_str} + except Exception: + pass + + return {"summary": "(파싱 실패)", "start": "", "end": ""} async def today() -> dict: