fix excel file message parsing and dingtalk fileType reply
This commit is contained in:
parent
fa631100ba
commit
3873928b66
@ -1446,16 +1446,37 @@ class OpenClawDingTalkBot(ChatbotHandler):
|
||||
return codes
|
||||
|
||||
def _normalize_file_name(self, raw_name: str) -> str:
|
||||
name = str(raw_name or "").strip().replace("\\", "_").replace("/", "_")
|
||||
name = re.sub(r"\s+", "_", name)
|
||||
name = re.sub(r"[^0-9A-Za-z._-]+", "_", name).strip("._")
|
||||
if not name:
|
||||
return f"upload_{int(time.time())}.xlsx"
|
||||
return name[:120]
|
||||
raw = str(raw_name or "").strip().replace("\\", "_").replace("/", "_")
|
||||
raw = re.sub(r"\s+", "_", raw)
|
||||
stem, ext = os.path.splitext(raw)
|
||||
safe_stem = re.sub(r"[^0-9A-Za-z._-]+", "_", stem).strip("._-")
|
||||
safe_ext = re.sub(r"[^0-9A-Za-z.]+", "", ext).lower()
|
||||
if safe_ext and not safe_ext.startswith("."):
|
||||
safe_ext = f".{safe_ext}"
|
||||
if not safe_stem:
|
||||
safe_stem = f"upload_{int(time.time())}"
|
||||
normalized = f"{safe_stem}{safe_ext}" if safe_ext else safe_stem
|
||||
return normalized[:120]
|
||||
|
||||
def _is_supported_excel_file(self, file_name: str) -> bool:
|
||||
return str(file_name or "").strip().lower().endswith(".xlsx")
|
||||
|
||||
def _is_supported_excel_payload(self, file_name: str, file_type: str) -> bool:
|
||||
if self._is_supported_excel_file(file_name):
|
||||
return True
|
||||
normalized = str(file_type or "").strip().lower()
|
||||
if not normalized:
|
||||
return False
|
||||
normalized = normalized.split(";")[0].strip()
|
||||
if normalized in {
|
||||
"xlsx",
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
}:
|
||||
return True
|
||||
if "spreadsheetml.sheet" in normalized:
|
||||
return True
|
||||
return False
|
||||
|
||||
def _is_excel_link_prompt(self, prompt: str) -> bool:
|
||||
raw = str(prompt or "").strip().lower()
|
||||
if not raw:
|
||||
@ -1480,16 +1501,45 @@ class OpenClawDingTalkBot(ChatbotHandler):
|
||||
continue
|
||||
if isinstance(current, dict):
|
||||
lowered = {str(k).lower(): k for k in current.keys()}
|
||||
code = ""
|
||||
if "downloadcode" in lowered:
|
||||
raw_code = current.get(lowered["downloadcode"])
|
||||
code = str(raw_code or "").strip()
|
||||
if code:
|
||||
|
||||
download_url = ""
|
||||
for key_name in (
|
||||
"downloadurl",
|
||||
"download_url",
|
||||
"privatedownloadurl",
|
||||
"private_download_url",
|
||||
"privateurl",
|
||||
):
|
||||
if key_name in lowered:
|
||||
raw_url = current.get(lowered[key_name])
|
||||
candidate_url = str(raw_url or "").strip()
|
||||
if candidate_url.lower().startswith(("http://", "https://")):
|
||||
download_url = candidate_url
|
||||
break
|
||||
|
||||
file_name = ""
|
||||
for candidate in ("fileName", "filename", "file_name", "name", "title"):
|
||||
if candidate in current and str(current.get(candidate) or "").strip():
|
||||
file_name = str(current.get(candidate)).strip()
|
||||
break
|
||||
return {"download_code": code, "file_name": file_name}
|
||||
|
||||
file_type = ""
|
||||
for candidate in ("fileType", "filetype", "mimeType", "mimetype", "suffix", "ext", "extension"):
|
||||
if candidate in current and str(current.get(candidate) or "").strip():
|
||||
file_type = str(current.get(candidate)).strip()
|
||||
break
|
||||
|
||||
if code or download_url:
|
||||
return {
|
||||
"download_code": code,
|
||||
"download_url": download_url,
|
||||
"file_name": file_name,
|
||||
"file_type": file_type,
|
||||
}
|
||||
for child in current.values():
|
||||
stack.append(child)
|
||||
continue
|
||||
@ -1509,7 +1559,16 @@ class OpenClawDingTalkBot(ChatbotHandler):
|
||||
payload = None
|
||||
if not payload:
|
||||
return {}
|
||||
|
||||
download_code = str(payload.get("download_code") or "").strip()
|
||||
download_url = str(payload.get("download_url") or "").strip()
|
||||
if download_url and not download_url.lower().startswith(("http://", "https://")):
|
||||
download_url = ""
|
||||
if not download_code and not download_url:
|
||||
return {}
|
||||
|
||||
file_name = payload.get("file_name") or ""
|
||||
file_type = payload.get("file_type") or ""
|
||||
if not file_name and isinstance(callback_data, dict):
|
||||
for key in ("fileName", "filename", "name", "title"):
|
||||
raw = callback_data.get(key)
|
||||
@ -1517,9 +1576,21 @@ class OpenClawDingTalkBot(ChatbotHandler):
|
||||
file_name = str(raw).strip()
|
||||
if file_name:
|
||||
break
|
||||
payload["file_name"] = self._normalize_file_name(file_name or "upload.xlsx")
|
||||
if not self._is_supported_excel_file(payload["file_name"]):
|
||||
if not file_type and isinstance(callback_data, dict):
|
||||
for key in ("fileType", "filetype", "mimeType", "mimetype", "suffix", "ext", "extension"):
|
||||
raw = callback_data.get(key)
|
||||
if raw:
|
||||
file_type = str(raw).strip()
|
||||
if file_type:
|
||||
break
|
||||
normalized_file_name = self._normalize_file_name(file_name or "upload.xlsx")
|
||||
normalized_file_type = str(file_type or "").strip().lower()
|
||||
if not self._is_supported_excel_payload(normalized_file_name, normalized_file_type):
|
||||
return {}
|
||||
payload["download_code"] = download_code
|
||||
payload["download_url"] = download_url
|
||||
payload["file_name"] = normalized_file_name
|
||||
payload["file_type"] = normalized_file_type
|
||||
return payload
|
||||
|
||||
def _guess_image_ext(self, content_type: str, source_url: str) -> str:
|
||||
@ -2200,9 +2271,11 @@ class OpenClawDingTalkBot(ChatbotHandler):
|
||||
raw = (proc.stdout or "").strip()
|
||||
return f"结果解析失败: {exc}.\n原始输出: {raw[:500]}"
|
||||
|
||||
def _reply_file_by_media_id(self, msg: ChatbotMessage, media_id: str) -> bool:
|
||||
def _reply_file_by_media_id(self, msg: ChatbotMessage, media_id: str, file_name: str = "") -> bool:
|
||||
if not media_id:
|
||||
return False
|
||||
safe_name = self._normalize_file_name(file_name or f"excel_{int(time.time())}.xlsx")
|
||||
file_ext = os.path.splitext(safe_name)[1].lower().lstrip(".") or "file"
|
||||
request_headers = {
|
||||
"Content-Type": "application/json",
|
||||
"Accept": "*/*",
|
||||
@ -2211,6 +2284,8 @@ class OpenClawDingTalkBot(ChatbotHandler):
|
||||
"msgtype": "file",
|
||||
"file": {
|
||||
"mediaId": media_id,
|
||||
"fileName": safe_name,
|
||||
"fileType": file_ext,
|
||||
},
|
||||
"at": {
|
||||
"atUserIds": [msg.sender_staff_id] if msg.sender_staff_id else [],
|
||||
@ -2337,12 +2412,14 @@ class OpenClawDingTalkBot(ChatbotHandler):
|
||||
return f"转换脚本不存在:{script_path}", False
|
||||
|
||||
download_code = str(file_payload.get("download_code") or "").strip()
|
||||
download_url = str(file_payload.get("download_url") or "").strip()
|
||||
file_name = self._normalize_file_name(file_payload.get("file_name") or "upload.xlsx")
|
||||
if not download_code:
|
||||
return "未获取到文件下载码,请重试上传。", False
|
||||
if not download_code and not download_url:
|
||||
return "未获取到文件下载地址,请重试上传。", False
|
||||
if not self._is_supported_excel_file(file_name):
|
||||
return "当前仅支持 .xlsx 文件,请上传Excel后重试。", False
|
||||
|
||||
if not download_url:
|
||||
download_url = self.get_image_download_url(download_code)
|
||||
if not download_url:
|
||||
return "文件下载地址获取失败,请重试。", False
|
||||
@ -2437,7 +2514,7 @@ class OpenClawDingTalkBot(ChatbotHandler):
|
||||
except Exception as exc:
|
||||
self.logger.exception("upload converted excel failed")
|
||||
|
||||
sent = self._reply_file_by_media_id(msg, media_id) if media_id else False
|
||||
sent = self._reply_file_by_media_id(msg, media_id, output_name) if media_id else False
|
||||
if not sent:
|
||||
s3_url = self._upload_excel_output_to_s3(output_path, output_name)
|
||||
if s3_url:
|
||||
@ -2588,6 +2665,15 @@ class OpenClawDingTalkBot(ChatbotHandler):
|
||||
bool(incoming_image_codes),
|
||||
len(text or ""),
|
||||
)
|
||||
if str(getattr(msg, "message_type", "")).lower() == "file" and not has_excel_file:
|
||||
callback_keys = []
|
||||
if isinstance(callback.data, dict):
|
||||
callback_keys = list(callback.data.keys())[:15]
|
||||
self.logger.warning(
|
||||
"file message without excel payload: callback_keys=%s extensions_keys=%s",
|
||||
callback_keys,
|
||||
list((getattr(msg, "extensions", {}) or {}).keys())[:15],
|
||||
)
|
||||
|
||||
if not text and not incoming_image_codes and not has_excel_file:
|
||||
return AckMessage.STATUS_OK, "ignored"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user