chore: rewrite feishu tool guard card comments as functional descriptions; remove unused wecom media helpers

This commit is contained in:
mateaix 2026-07-21 21:55:39 +08:00
parent be836b6f0a
commit 2405c7f3d6
3 changed files with 9 additions and 75 deletions

View File

@ -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;

View File

@ -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<String, Object> 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.
*
* <p>Caller passes the resulting Map to a {@code CallBackCard}
* with {@code type="raw"} (NOT {@code card_json}).

View File

@ -3243,63 +3243,12 @@ public class WeComChannelAdapter extends AbstractChannelAdapter implements Strea
}
/**
* 下载并解密企业微信媒体文件旧版本保留给 outbound / 其他场景使用
* AES-256-CBC decryption for WeCom media payloads.
* <p>
* 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<InputStream> 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
* <p>
* 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 哈希字节数组输入
*/