From 2405c7f3d6535b883d8edbe51906c8d73e2d975f Mon Sep 17 00:00:00 2001 From: mateaix <57164338+mateaix@users.noreply.github.com> Date: Tue, 21 Jul 2026 21:55:39 +0800 Subject: [PATCH] chore: rewrite feishu tool guard card comments as functional descriptions; remove unused wecom media helpers --- .../tool_guard/ToolGuardCardHandler.java | 4 +- .../tool_guard/ToolGuardCardRenderer.java | 6 +- .../channel/wecom/WeComChannelAdapter.java | 74 ++----------------- 3 files changed, 9 insertions(+), 75 deletions(-) diff --git a/mateclaw-server/src/main/java/vip/mate/channel/feishu/cards/tool_guard/ToolGuardCardHandler.java b/mateclaw-server/src/main/java/vip/mate/channel/feishu/cards/tool_guard/ToolGuardCardHandler.java index c6c85d4f..41c7ed98 100644 --- a/mateclaw-server/src/main/java/vip/mate/channel/feishu/cards/tool_guard/ToolGuardCardHandler.java +++ b/mateclaw-server/src/main/java/vip/mate/channel/feishu/cards/tool_guard/ToolGuardCardHandler.java @@ -284,8 +284,8 @@ public class ToolGuardCardHandler implements FeishuCardHandler { // Schema 2.0 body works fine on cardkit/v1 card.create and on // im/v1 message.create msg_type=interactive. Two different // server-side validators, only one of which has been upgraded - // for Schema 2.0. QwenPaw's production Feishu adapter uses the - // same type="raw" approach for callback updates. + // for Schema 2.0, so callback updates must go through + // type="raw" with a Schema 1.0 body. cb.setType("raw"); cb.setData(cardJson); return cb; diff --git a/mateclaw-server/src/main/java/vip/mate/channel/feishu/cards/tool_guard/ToolGuardCardRenderer.java b/mateclaw-server/src/main/java/vip/mate/channel/feishu/cards/tool_guard/ToolGuardCardRenderer.java index 7ab29305..5c28ed82 100644 --- a/mateclaw-server/src/main/java/vip/mate/channel/feishu/cards/tool_guard/ToolGuardCardRenderer.java +++ b/mateclaw-server/src/main/java/vip/mate/channel/feishu/cards/tool_guard/ToolGuardCardRenderer.java @@ -74,8 +74,7 @@ public class ToolGuardCardRenderer implements FeishuCardRenderer { // mismatch error. Schema 2.0 is supported by im/v1/message.create // BUT the callback response validator only accepts Schema 1.0 // inline (type="raw") — once we commit to Schema 1.0 here the - // resolved-state card update lands cleanly. QwenPaw's - // production Feishu integration uses the same Schema 1.0 path. + // resolved-state card update lands cleanly. Map approveBtn = new LinkedHashMap<>(); approveBtn.put("tag", "button"); approveBtn.put("text", plainText("批准")); @@ -116,8 +115,7 @@ public class ToolGuardCardRenderer implements FeishuCardRenderer { * {@code cardkit/v1 card.create}, both of which DO accept Schema * 2.0. So we keep the original approval card (sent via message * create) in Schema 2.0 for the column_set button layout, but the - * resolved-state update has to be Schema 1.0. QwenPaw's production - * Feishu integration uses the same split. + * resolved-state update has to be Schema 1.0. * *

Caller passes the resulting Map to a {@code CallBackCard} * with {@code type="raw"} (NOT {@code card_json}). diff --git a/mateclaw-server/src/main/java/vip/mate/channel/wecom/WeComChannelAdapter.java b/mateclaw-server/src/main/java/vip/mate/channel/wecom/WeComChannelAdapter.java index ea447426..45b87083 100644 --- a/mateclaw-server/src/main/java/vip/mate/channel/wecom/WeComChannelAdapter.java +++ b/mateclaw-server/src/main/java/vip/mate/channel/wecom/WeComChannelAdapter.java @@ -3243,63 +3243,12 @@ public class WeComChannelAdapter extends AbstractChannelAdapter implements Strea } /** - * 下载并解密企业微信媒体文件(旧版本,保留给 outbound / 其他场景使用) + * AES-256-CBC decryption for WeCom media payloads. *

- * AES-256-CBC 解密:base64 decode aesKey → IV = 前 16 字节 → PKCS#7 去填充 - * - * @param url 文件下载 URL - * @param aesKey Base64 编码的 AES-256 密钥 - * @param msgId 消息 ID(用于生成文件名) - * @param fileNameHint 文件名提示 - * @return 本地文件路径,失败返回 null - */ - private String downloadAndDecryptMedia(String url, String aesKey, String msgId, String fileNameHint) { - try { - String mediaDir = getConfigString("media_dir", "data/media"); - Path mediaDirPath = Path.of(mediaDir); - Files.createDirectories(mediaDirPath); - - // 1. HTTP GET 下载文件 - HttpRequest request = HttpRequest.newBuilder() - .uri(URI.create(url)) - .timeout(Duration.ofSeconds(30)) - .GET() - .build(); - - HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofInputStream()); - byte[] encryptedData = response.body().readAllBytes(); - - byte[] fileData; - // 2. AES 解密(如果提供了 aesKey) - if (aesKey != null && !aesKey.isBlank()) { - fileData = decryptAes256Cbc(encryptedData, aesKey); - } else { - fileData = encryptedData; - } - - // 3. 保存到本地 - String urlHash = md5Hex(url).substring(0, 8); - String safeName = fileNameHint.replaceAll("[^a-zA-Z0-9._-]", "_"); - if (safeName.isBlank()) safeName = "media"; - Path filePath = mediaDirPath.resolve("wecom_" + urlHash + "_" + safeName); - Files.write(filePath, fileData); - - log.info("[wecom] Media downloaded: {} ({} bytes)", filePath, fileData.length); - return filePath.toAbsolutePath().toString(); - - } catch (Exception e) { - log.error("[wecom] Failed to download media: {}", e.getMessage(), e); - return null; - } - } - - /** - * AES-256-CBC 解密(对齐 wecom-aibot-python-sdk crypto_utils.py) - *

- * 1. Base64 decode aesKey(自动补齐 padding) - * 2. IV = decoded key 前 16 字节 - * 3. AES-256-CBC 解密 - * 4. PKCS#7 去填充 + * 1. Base64 decode aesKey (auto-fix missing padding) + * 2. IV = first 16 bytes of the decoded key + * 3. AES-256-CBC decrypt + * 4. Strip PKCS#7 padding */ private byte[] decryptAes256Cbc(byte[] encryptedData, String aesKeyBase64) throws Exception { // 补齐 Base64 padding @@ -3377,19 +3326,6 @@ public class WeComChannelAdapter extends AbstractChannelAdapter implements Strea return prefix + "_" + System.currentTimeMillis() + "_" + reqIdCounter.incrementAndGet(); } - /** - * MD5 哈希(hex 字符串) - */ - private String md5Hex(String input) { - try { - MessageDigest md = MessageDigest.getInstance("MD5"); - byte[] hash = md.digest(input.getBytes()); - return bytesToHex(hash); - } catch (Exception e) { - return Integer.toHexString(input.hashCode()); - } - } - /** * MD5 哈希(字节数组输入) */