diff --git a/remote_sync/bot.py b/remote_sync/bot.py index b69f35d..12cf36b 100755 --- a/remote_sync/bot.py +++ b/remote_sync/bot.py @@ -152,6 +152,17 @@ DALONGXIA_ANALYTICS_HINT_KEYWORDS = ( "analytics", ) +EXCEL_IMAGE_LINK_HINT_KEYWORDS = ( + "图片链接", + "链接转图片", + "转成图片", + "回填", + "excel", + "xlsx", + "表格", + "文件", +) + URL_RE = re.compile( r"https?://[A-Za-z0-9\-._~:/?#\[\]@!$&'()*+,;=%]+", @@ -313,6 +324,11 @@ class OpenClawDingTalkBot(ChatbotHandler): self.excel_image_download_timeout = max(3, min(int(os.getenv("EXCEL_IMAGE_DOWNLOAD_TIMEOUT", "20")), 120)) self.excel_file_download_timeout = max(5, min(int(os.getenv("EXCEL_FILE_DOWNLOAD_TIMEOUT", "60")), 300)) self.excel_skip_tls_verify = bool_env("EXCEL_IMAGE_SKIP_TLS_VERIFY", False) + self.excel_text_prompt_cooldown_seconds = max( + 10, + min(int(os.getenv("EXCEL_TEXT_PROMPT_COOLDOWN_SECONDS", "120")), 600), + ) + self._recent_excel_success_ts = {} def _should_handle(self, msg: ChatbotMessage, text: str) -> bool: if text.startswith(self.prefix): @@ -1429,6 +1445,12 @@ class OpenClawDingTalkBot(ChatbotHandler): def _is_supported_excel_file(self, file_name: str) -> bool: return str(file_name or "").strip().lower().endswith(".xlsx") + def _is_excel_link_prompt(self, prompt: str) -> bool: + raw = str(prompt or "").strip().lower() + if not raw: + return False + return any(keyword in raw for keyword in EXCEL_IMAGE_LINK_HINT_KEYWORDS) + def _find_download_code_payload(self, value): stack = [value] while stack: @@ -2423,6 +2445,7 @@ class OpenClawDingTalkBot(ChatbotHandler): incoming_image_codes = self._extract_incoming_download_codes(msg) incoming_file_payload = self._extract_incoming_file_payload(callback.data, msg) has_excel_file = bool(incoming_file_payload) + conversation_key = re.sub(r"[^A-Za-z0-9_-]+", "-", str(getattr(msg, "conversation_id", "") or "default")) if not text and not incoming_image_codes and not has_excel_file: return AckMessage.STATUS_OK, "ignored" @@ -2443,6 +2466,14 @@ class OpenClawDingTalkBot(ChatbotHandler): prefer_card=False, ) return AckMessage.STATUS_OK, "ok" + elif not has_excel_file and self.excel_link_feature_enabled and self._is_excel_link_prompt(prompt): + now_ts = time.time() + last_ts = self._recent_excel_success_ts.get(conversation_key, 0) + if last_ts and now_ts - last_ts <= self.excel_text_prompt_cooldown_seconds: + self._reply_message(msg, "文件刚处理完成,请查看上一条回传文件。", prefer_card=False) + else: + self._reply_message(msg, "请直接上传 .xlsx 文件(包含“图片链接”列),我会自动回填并回传。", prefer_card=False) + return AckMessage.STATUS_OK, "excel_text_prompt_hint" event_key = self._event_key(callback, msg, prompt) if self._seen_or_mark_event(event_key): @@ -2468,7 +2499,6 @@ class OpenClawDingTalkBot(ChatbotHandler): except Exception: self.logger.exception("failed to send processing hint") - conversation_key = re.sub(r"[^A-Za-z0-9_-]+", "-", str(getattr(msg, "conversation_id", "") or "default")) conv_lock = self._conversation_locks.setdefault(conversation_key, asyncio.Lock()) async with conv_lock: @@ -2490,6 +2520,7 @@ class OpenClawDingTalkBot(ChatbotHandler): incoming_file_payload, ) if file_sent: + self._recent_excel_success_ts[conversation_key] = time.time() self._reply_message(msg, file_answer, prefer_card=False) self.logger.info( "excel file processed: session=%s file=%s",