From d085a3c7d7a58079b1ec7e8ec82d1b8bcd4db00e Mon Sep 17 00:00:00 2001 From: zhayujie Date: Mon, 2 Feb 2026 11:58:19 +0800 Subject: [PATCH] fix: dingtalk picture and file process --- channel/dingtalk/dingtalk_channel.py | 41 ++++++------- channel/dingtalk/dingtalk_message.py | 90 +++++++++++++++++++--------- config.py | 6 +- requirements-optional.txt | 3 - requirements.txt | 4 +- 5 files changed, 87 insertions(+), 57 deletions(-) diff --git a/channel/dingtalk/dingtalk_channel.py b/channel/dingtalk/dingtalk_channel.py index 1b1e71b..ad7edd0 100644 --- a/channel/dingtalk/dingtalk_channel.py +++ b/channel/dingtalk/dingtalk_channel.py @@ -437,38 +437,31 @@ class DingTalkChanel(ChatChannel, dingtalk_stream.ChatbotHandler): def get_image_download_url(self, download_code: str) -> str: """ 获取图片下载地址 - 使用钉钉 API: https://open.dingtalk.com/document/orgapp/download-the-robot-to-receive-the-file + 返回一个特殊的 URL 格式:dingtalk://download/{robot_code}:{download_code} + 后续会在 download_image_file 中使用新版 API 下载 """ - access_token = self.get_access_token() - if not access_token: - logger.error("[DingTalk] Cannot get access token for image download") + # 获取 robot_code + if not hasattr(self, '_robot_code_cache'): + self._robot_code_cache = None + + robot_code = self._robot_code_cache + + if not robot_code: + logger.error("[DingTalk] robot_code not available for image download") return None - url = f"https://oapi.dingtalk.com/robot/messageFiles/download" - params = { - "access_token": access_token, - "downloadCode": download_code - } - - try: - response = requests.get(url, params=params, timeout=10) - if response.status_code == 200: - # 返回图片的直接下载 URL(实际上这个 API 直接返回文件内容) - # 我们需要保存文件并返回本地路径 - logger.info(f"[DingTalk] Successfully got image download URL for code: {download_code}") - # 返回一个特殊的 URL,包含 download_code,后续会用它来下载 - return f"dingtalk://download/{download_code}" - else: - logger.error(f"[DingTalk] Failed to get image download URL: {response.text}") - return None - except Exception as e: - logger.error(f"[DingTalk] Exception getting image download URL: {e}") - return None + # 返回一个特殊的 URL,包含 robot_code 和 download_code + logger.info(f"[DingTalk] Successfully got image download URL for code: {download_code}") + return f"dingtalk://download/{robot_code}:{download_code}" async def process(self, callback: dingtalk_stream.CallbackMessage): try: incoming_message = dingtalk_stream.ChatbotMessage.from_dict(callback.data) + # 缓存 robot_code,用于后续图片下载 + if hasattr(incoming_message, 'robot_code'): + self._robot_code_cache = incoming_message.robot_code + # Debug: 打印完整的 event 数据 logger.debug(f"[DingTalk] ===== Incoming Message Debug =====") logger.debug(f"[DingTalk] callback.data keys: {callback.data.keys() if hasattr(callback.data, 'keys') else 'N/A'}") diff --git a/channel/dingtalk/dingtalk_message.py b/channel/dingtalk/dingtalk_message.py index 9754f32..88fb88b 100644 --- a/channel/dingtalk/dingtalk_message.py +++ b/channel/dingtalk/dingtalk_message.py @@ -138,48 +138,84 @@ def download_image_file(image_url, temp_dir): logger.error("[DingTalk] Missing dingtalk_client_id or dingtalk_client_secret") return None - # 获取 access_token - token_url = "https://oapi.dingtalk.com/gettoken" - token_params = { - "appkey": client_id, - "appsecret": client_secret + # 解析 robot_code 和 download_code + parts = download_code.split(":", 1) + if len(parts) != 2: + logger.error(f"[DingTalk] Invalid download_code format (expected robot_code:download_code): {download_code[:50]}") + return None + + robot_code, actual_download_code = parts + + # 获取 access_token(使用新版 API) + token_url = "https://api.dingtalk.com/v1.0/oauth2/accessToken" + token_headers = { + "Content-Type": "application/json" + } + token_body = { + "appKey": client_id, + "appSecret": client_secret } try: - token_response = requests.get(token_url, params=token_params, timeout=10) - token_data = token_response.json() + token_response = requests.post(token_url, json=token_body, headers=token_headers, timeout=10) - if token_data.get("errcode") == 0: - access_token = token_data.get("access_token") + if token_response.status_code == 200: + token_data = token_response.json() + access_token = token_data.get("accessToken") - # 下载图片 - download_url = f"https://oapi.dingtalk.com/robot/messageFiles/download" - download_params = { - "access_token": access_token, - "downloadCode": download_code + if not access_token: + logger.error(f"[DingTalk] Failed to get access token: {token_data}") + return None + + # 获取下载 URL(使用新版 API) + download_api_url = "https://api.dingtalk.com/v1.0/robot/messageFiles/download" + download_headers = { + "x-acs-dingtalk-access-token": access_token, + "Content-Type": "application/json" + } + download_body = { + "downloadCode": actual_download_code, + "robotCode": robot_code } - response = requests.get(download_url, params=download_params, stream=True, timeout=60) - if response.status_code == 200: - # 生成文件名(使用 download_code 的 hash,避免特殊字符) - import hashlib - file_hash = hashlib.md5(download_code.encode()).hexdigest()[:16] - file_name = f"{file_hash}.png" - file_path = os.path.join(temp_dir, file_name) + download_response = requests.post(download_api_url, json=download_body, headers=download_headers, timeout=10) + + if download_response.status_code == 200: + download_data = download_response.json() + download_url = download_data.get("downloadUrl") - with open(file_path, 'wb') as file: - file.write(response.content) + if not download_url: + logger.error(f"[DingTalk] No downloadUrl in response: {download_data}") + return None - logger.info(f"[DingTalk] Image downloaded successfully: {file_path}") - return file_path + # 从 downloadUrl 下载实际图片 + image_response = requests.get(download_url, stream=True, timeout=60) + + if image_response.status_code == 200: + # 生成文件名(使用 download_code 的 hash,避免特殊字符) + import hashlib + file_hash = hashlib.md5(actual_download_code.encode()).hexdigest()[:16] + file_name = f"{file_hash}.png" + file_path = os.path.join(temp_dir, file_name) + + with open(file_path, 'wb') as file: + file.write(image_response.content) + + logger.info(f"[DingTalk] Image downloaded successfully: {file_path}") + return file_path + else: + logger.error(f"[DingTalk] Failed to download image from URL: {image_response.status_code}") + return None else: - logger.error(f"[DingTalk] Failed to download image: {response.status_code}") + logger.error(f"[DingTalk] Failed to get download URL: {download_response.status_code}, {download_response.text}") return None else: - logger.error(f"[DingTalk] Failed to get access token: {token_data}") + logger.error(f"[DingTalk] Failed to get access token: {token_response.status_code}, {token_response.text}") return None except Exception as e: logger.error(f"[DingTalk] Exception downloading image: {e}") + import traceback + logger.error(traceback.format_exc()) return None # 普通 HTTP(S) URL diff --git a/config.py b/config.py index ecd3b72..d47aa1a 100644 --- a/config.py +++ b/config.py @@ -187,9 +187,11 @@ available_setting = { "Minimax_group_id": "", "Minimax_base_url": "", "web_port": 9899, - "agent": False, # 是否开启Agent模式 + "agent": True, # 是否开启Agent模式 "agent_workspace": "~/cow", # agent工作空间路径,用于存储skills、memory等 - "bocha_api_key": "" + "agent_max_context_tokens": 40000, # Agent模式下最大上下文tokens + "agent_max_context_turns": 30, # Agent模式下最大上下文轮次 + "agent_max_steps": 20, # Agent模式下单次运行最大决策步数 } diff --git a/requirements-optional.txt b/requirements-optional.txt index 8d86cda..adc2385 100644 --- a/requirements-optional.txt +++ b/requirements-optional.txt @@ -32,9 +32,6 @@ broadscope_bailian # google google-generativeai -# dingtalk -dingtalk_stream - # zhipuai zhipuai>=2.0.1 diff --git a/requirements.txt b/requirements.txt index 848acd2..9dc82bd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,4 +14,6 @@ PyYAML>=6.0 croniter>=2.0.0 # feishu websocket mode -lark-oapi \ No newline at end of file +lark-oapi +# dingtalk +dingtalk_stream