Files
nextcloud-mcp-server/nextcloud_mcp_server/notes_client.py
T
2025-07-06 08:39:28 +02:00

171 lines
6.6 KiB
Python

"""Client for Nextcloud Notes app operations."""
from typing import Dict, List, Any, Optional
import logging
from .base_client import BaseNextcloudClient
logger = logging.getLogger(__name__)
class NotesClient(BaseNextcloudClient):
"""Client for Nextcloud Notes app operations."""
async def get_settings(self) -> Dict[str, Any]:
"""Get Notes app settings."""
response = await self._make_request("GET", "/apps/notes/api/v1/settings")
return response.json()
async def get_all_notes(self) -> List[Dict[str, Any]]:
"""Get all notes."""
response = await self._make_request("GET", "/apps/notes/api/v1/notes")
return response.json()
async def get_note(self, note_id: int) -> Dict[str, Any]:
"""Get a specific note by ID."""
response = await self._make_request("GET", f"/apps/notes/api/v1/notes/{note_id}")
return response.json()
async def create_note(
self,
title: Optional[str] = None,
content: Optional[str] = None,
category: Optional[str] = None,
) -> Dict[str, Any]:
"""Create a new note."""
body = {}
if title:
body["title"] = title
if content:
body["content"] = content
if category:
body["category"] = category
response = await self._make_request("POST", "/apps/notes/api/v1/notes", json=body)
return response.json()
async def update(
self,
note_id: int,
etag: str,
title: Optional[str] = None,
content: Optional[str] = None,
category: Optional[str] = None,
) -> Dict[str, Any]:
"""Update an existing note."""
# Get current note details to check for category change
old_note = None
try:
if category is not None:
old_note = await self.get_note(note_id)
old_category = old_note.get("category", "")
logger.info(f"Current category for note {note_id}: '{old_category}'")
except Exception as e:
logger.warning(f"Could not fetch current note {note_id} details before update: {e}")
old_note = None
# Prepare update body
body = {}
if title:
body["title"] = title
if content:
body["content"] = content
if category:
body["category"] = category
logger.info(f"Attempting to update note {note_id} with etag {etag}. Body: {body}")
response = await self._make_request(
"PUT",
f"/apps/notes/api/v1/notes/{note_id}",
json=body,
headers={"If-Match": f'"{etag}"'}
)
logger.info(f"Update response for note {note_id}: Status {response.status_code}")
updated_note = response.json()
# Check for category change and cleanup old attachment directory if needed
if old_note and category is not None and old_note.get("category", "") != category:
logger.info(f"Category changed from '{old_note.get('category', '')}' to '{category}' - cleaning up old attachment directory")
try:
# Import here to avoid circular imports
from .webdav_client import WebDAVClient
webdav_client = WebDAVClient(self._client, self.username)
await webdav_client.cleanup_old_attachment_directory(
note_id=note_id,
old_category=old_note.get("category", "")
)
except Exception as e:
logger.error(f"Error cleaning up old attachment directory for note {note_id}: {e}")
return updated_note
async def delete_note(self, note_id: int) -> Dict[str, Any]:
"""Delete a note and its attachments."""
# Fetch note details first to get category for cleanup
try:
note_details = await self.get_note(note_id)
category = note_details.get("category", "")
# Determine potential categories for cleanup
potential_categories = []
if category:
potential_categories.append(category)
if category != "":
potential_categories.append("") # Empty category
logger.info(f"Note {note_id} has category: '{category}', will check attachment directories in: {potential_categories}")
except Exception as e:
logger.warning(f"Could not fetch note {note_id} details before deletion: {e}")
potential_categories = ["", "Unknown"] # Try common categories
# Delete the note via API
logger.info(f"Deleting note {note_id} via API")
response = await self._make_request("DELETE", f"/apps/notes/api/v1/notes/{note_id}")
logger.info(f"Note {note_id} deleted successfully via API")
json_response = response.json()
# Clean up attachment directories
try:
from .webdav_client import WebDAVClient
webdav_client = WebDAVClient(self._client, self.username)
for cat in potential_categories:
try:
await webdav_client.cleanup_note_attachments(note_id, cat)
except Exception as e:
logger.warning(f"Failed to cleanup attachments for category '{cat}': {e}")
except Exception as e:
logger.warning(f"Error during attachment cleanup: {e}")
return json_response
async def append_content(self, note_id: int, content: str) -> Dict[str, Any]:
"""Append content to an existing note with a separator."""
logger.info(f"Appending content to note {note_id}")
# Get current note
current_note = await self.get_note(note_id)
# Use fixed separator for consistency
separator = "\n---\n"
# Combine content
existing_content = current_note.get("content", "")
if existing_content:
new_content = existing_content + separator + content
else:
new_content = content # No separator needed for empty notes
logger.info(f"Combining existing content ({len(existing_content)} chars) with new content ({len(content)} chars)")
# Update with combined content
return await self.update(
note_id=note_id,
etag=current_note["etag"],
content=new_content,
title=None, # Keep existing title
category=None, # Keep existing category
)