Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 83 additions & 4 deletions Plugin/VCPClawMail/VCPClawMail.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ const crypto = require('crypto');
const axios = require('axios');
const mime = require('mime-types');
const TurndownService = require('turndown');

let HttpsProxyAgent = null;
try { HttpsProxyAgent = require('https-proxy-agent').HttpsProxyAgent; } catch (_) {}
const { fileURLToPath } = require('url');
const pluginManager = require('../../Plugin.js');

Expand Down Expand Up @@ -77,6 +80,44 @@ function splitList(value) {
return [String(value).trim()].filter(Boolean);
}

function getProxyUrl() {
return config.ClawMailProxy || process.env.ClawMailProxy || process.env.HTTPS_PROXY || process.env.https_proxy || process.env.HTTP_PROXY || process.env.http_proxy || '';
}

function parseProxyUrl(url) {
if (!url) return null;
try {
return new URL(url);
} catch (_) {
return null;
}
}

function createProxyAgent(proxyUrl) {
if (!proxyUrl || !HttpsProxyAgent) return null;
const parsed = parseProxyUrl(proxyUrl);
if (!parsed) return null;
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') return null;
try {
return new HttpsProxyAgent(proxyUrl);
} catch (error) {
warn('创建 HTTPS 代理 Agent 失败:', error.message);
return null;
}
}

function maskProxyUrl(url) {
if (!url) return '';
try {
const u = new URL(url);
if (u.username) u.username = '***';
if (u.password) u.password = '***';
return u.toString();
} catch (_) {
return url;
}
}

function getUsers() {
const users = splitList(config.ClawMailUsers);
const defaultUser = String(config.ClawMailDefaultUser || '').trim();
Expand Down Expand Up @@ -111,6 +152,19 @@ function getClient(user) {
apiKey,
user: normalizedUser
});
const proxyUrl = getProxyUrl();
if (proxyUrl) {
const agent = createProxyAgent(proxyUrl);
if (agent) {
if (client.http && client.http.defaults) {
client.http.defaults.httpsAgent = agent;
client.http.defaults.proxy = false;
}
log(`已为用户 ${normalizedUser} 配置网络代理: ${maskProxyUrl(proxyUrl)}`);
} else {
warn('配置的代理无法解析或不被支持(仅支持 http/https 代理):', maskProxyUrl(proxyUrl));
}
}
clients.set(normalizedUser, client);
}
return clients.get(normalizedUser);
Expand Down Expand Up @@ -467,11 +521,13 @@ async function listEmails(args = {}) {
lines.push('');
lines.push('如需读取正文,请用 `read_mail` 并传入表格中的 `mailId`。');

return asAiText(lines.join('\n'), {
const aiResult = asAiText(lines.join('\n'), {
command: 'list_recent',
user,
count: emails.length
});
aiResult.emails = emails;
return aiResult;
}

async function attachmentResponseToBuffer(response) {
Expand Down Expand Up @@ -744,12 +800,34 @@ async function downloadUrlToAttachment(url) {
};
}

const response = await axios.get(url, {
const axiosOptions = {
responseType: 'arraybuffer',
timeout: 60_000,
maxContentLength: MAX_ATTACHMENT_BYTES,
maxBodyLength: MAX_ATTACHMENT_BYTES
});
};

const proxyUrl = getProxyUrl();
if (proxyUrl) {
const targetProtocol = new URL(url).protocol;
const agent = createProxyAgent(proxyUrl);
if (targetProtocol === 'https:' && agent) {
axiosOptions.httpsAgent = agent;
axiosOptions.proxy = false;
} else {
const parsed = parseProxyUrl(proxyUrl);
if (parsed && (parsed.protocol === 'http:' || parsed.protocol === 'https:')) {
axiosOptions.proxy = {
protocol: parsed.protocol.replace(':', ''),
host: parsed.hostname,
port: parsed.port || (parsed.protocol === 'https:' ? 443 : 80),
auth: parsed.username ? { username: parsed.username, password: parsed.password } : undefined
};
}
}
}

const response = await axios.get(url, axiosOptions);
const contentType = response.headers['content-type'] || 'application/octet-stream';
const urlPath = new URL(url).pathname;
const ext = mime.extension(contentType) || path.extname(urlPath).replace(/^\./, '') || 'bin';
Expand Down Expand Up @@ -1143,6 +1221,7 @@ async function processToolCall(params = {}) {
'# ClawEmail 插件状态',
'',
`- SDK 已加载:${mdBool(Boolean(MailClient))}`,
`- 网络代理:${maskProxyUrl(getProxyUrl()) || '无'}`,
`- 配置邮箱:${getUsers().join(', ') || '无'}`,
`- 默认邮箱:${config.ClawMailDefaultUser || getUsers()[0] || '无'}`,
`- 缓存更新时间:${cache.updatedAt || '尚未完成首次轮询'}`,
Expand Down Expand Up @@ -1181,4 +1260,4 @@ module.exports = {
normalizeAttachmentInputs,
splitList
}
};
};
5 changes: 5 additions & 0 deletions Plugin/VCPClawMail/config.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,8 @@ ClawMailAutoMarkRead=false

# 可选:调试日志。
DebugMode=false

# 可选:网络代理。用于 SDK 请求(ClawEmail API)和附件下载。
# 支持 http/https 代理,格式如 http://host:port 或 http://user:pass@host:port
# 也支持读取系统环境变量 HTTPS_PROXY / HTTP_PROXY。
ClawMailProxy=
3 changes: 2 additions & 1 deletion Plugin/VCPClawMail/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@
"@clawemail/node-sdk": "0.2.4",
"axios": "^1.6.0",
"cheerio": "^1.1.2",
"https-proxy-agent": "^5.0.1",
"mime-types": "^3.0.1",
"turndown": "^7.2.1",
"tweetnacl": "^1.0.3"
}
}
}
Loading