添加完整的微信插件测试框架,包含: - 微信插件源码(weixin-plugin/) - TypeScript 类型定义(types.ts) - 插件编译脚本(scripts/build-weixin-plugin.sh) - 简化测试脚本(scripts/test-weixin-simple.js) - 完整测试脚本(scripts/test-weixin-plugin.js) - 项目配置和依赖管理(package.json) - 使用说明文档(README.md) 主要功能: - 支持二维码登录认证 - HTTP 长轮询消息接收 - 文本、图片、视频、文件消息收发 - 自动回复测试 - 运行状态统计 Generated with [Claude Code](https://claude.com/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>
55 lines
1.8 KiB
TypeScript
55 lines
1.8 KiB
TypeScript
const DEFAULT_BODY_MAX_LEN = 200;
|
|
const DEFAULT_TOKEN_PREFIX_LEN = 6;
|
|
|
|
/**
|
|
* Truncate a string, appending a length indicator when trimmed.
|
|
* Returns `""` for empty/undefined input.
|
|
*/
|
|
export function truncate(s: string | undefined, max: number): string {
|
|
if (!s) return "";
|
|
if (s.length <= max) return s;
|
|
return `${s.slice(0, max)}…(len=${s.length})`;
|
|
}
|
|
|
|
/**
|
|
* Redact a token/secret: show only the first few chars + total length.
|
|
* Returns `"(none)"` when absent.
|
|
*/
|
|
export function redactToken(token: string | undefined, prefixLen = DEFAULT_TOKEN_PREFIX_LEN): string {
|
|
if (!token) return "(none)";
|
|
if (token.length <= prefixLen) return `****(len=${token.length})`;
|
|
return `${token.slice(0, prefixLen)}…(len=${token.length})`;
|
|
}
|
|
|
|
/** Field names whose values should be masked in logged JSON bodies. */
|
|
const SENSITIVE_FIELDS = /\b(context_token|bot_token|token|authorization|Authorization)\b/;
|
|
|
|
/**
|
|
* Truncate a JSON body string to `maxLen` chars for safe logging.
|
|
* Redacts known sensitive fields before truncating.
|
|
*/
|
|
export function redactBody(body: string | undefined, maxLen = DEFAULT_BODY_MAX_LEN): string {
|
|
if (!body) return "(empty)";
|
|
// Mask values of known sensitive JSON keys: "key":"value" → "key":"<redacted>"
|
|
const redacted = body.replace(
|
|
/"(context_token|bot_token|token|authorization|Authorization)"\s*:\s*"[^"]*"/g,
|
|
'"$1":"<redacted>"',
|
|
);
|
|
if (redacted.length <= maxLen) return redacted;
|
|
return `${redacted.slice(0, maxLen)}…(truncated, totalLen=${redacted.length})`;
|
|
}
|
|
|
|
/**
|
|
* Strip query string (which often contains signatures/tokens) from a URL,
|
|
* keeping only origin + pathname.
|
|
*/
|
|
export function redactUrl(rawUrl: string): string {
|
|
try {
|
|
const u = new URL(rawUrl);
|
|
const base = `${u.origin}${u.pathname}`;
|
|
return u.search ? `${base}?<redacted>` : base;
|
|
} catch {
|
|
return truncate(rawUrl, 80);
|
|
}
|
|
}
|