# Copyright (c) 2025 Patrick Motsch # All rights reserved. import base64 import json import logging from typing import Any, Dict from modules.datamodels.datamodelChat import ActionDocument, ActionResult from ..helpers.pathparse import parse_task_id logger = logging.getLogger(__name__) async def upload_attachment(self, parameters: Dict[str, Any]) -> ActionResult: connection_reference = parameters.get("connectionReference") task_id = (parameters.get("taskId") or "").strip() path_hint = (parameters.get("path") or "").strip() if not connection_reference: return ActionResult.isFailure(error="connectionReference is required") if not task_id and path_hint: task_id = parse_task_id(path_hint) or "" if not task_id: return ActionResult.isFailure(error="taskId is required") conn = self.connection.get_clickup_connection(connection_reference) if not conn: return ActionResult.isFailure(error="No valid ClickUp connection") content_param = parameters.get("content") if not content_param: return ActionResult.isFailure(error="content is required (connect a file node upstream)") content = content_param[0] if isinstance(content_param, (list, tuple)) and content_param else content_param file_name = parameters.get("fileName") file_bytes = None if isinstance(content, dict): file_name = file_name or content.get("documentName") or content.get("fileName") or "attachment" raw_data = content.get("documentData") if (content.get("validationMetadata") or {}).get("fileId") and not raw_data: fid = content["validationMetadata"]["fileId"] try: raw = self.services.chat.getFileData(fid) file_bytes = raw if isinstance(raw, bytes) else str(raw).encode("utf-8") except Exception as e: return ActionResult.isFailure(error=f"Could not load file {fid}: {e}") elif raw_data is not None: if isinstance(raw_data, bytes): file_bytes = raw_data elif isinstance(raw_data, str): try: file_bytes = base64.b64decode(raw_data) except Exception: file_bytes = raw_data.encode("utf-8") else: return ActionResult.isFailure(error="Unsupported documentData type") else: return ActionResult.isFailure(error="Could not read file bytes from content") elif hasattr(content, "documentData"): file_name = file_name or getattr(content, "documentName", None) or getattr(content, "fileName", None) or "attachment" raw_data = content.documentData if isinstance(raw_data, bytes): file_bytes = raw_data elif isinstance(raw_data, str): try: file_bytes = base64.b64decode(raw_data) except Exception: file_bytes = raw_data.encode("utf-8") else: return ActionResult.isFailure(error="Unsupported documentData on ActionDocument") else: return ActionResult.isFailure(error="Unsupported content format") if not file_bytes: return ActionResult.isFailure(error="Empty file content") data = await self.services.clickup.uploadTaskAttachment(task_id, file_bytes, file_name or "file") if isinstance(data, dict) and data.get("error"): return ActionResult.isFailure(error=str(data.get("error")) + (data.get("body") or "")) doc = ActionDocument( documentName="clickup_upload_attachment.json", documentData=json.dumps(data, ensure_ascii=False, indent=2), mimeType="application/json", validationMetadata={"actionType": "clickup.uploadAttachment", "taskId": task_id}, ) return ActionResult.isSuccess(documents=[doc])