daodou-command 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.daodourc +28 -0
- package/README.md +122 -0
- package/bin/daodou.js +96 -0
- package/lib/commands/build.js +423 -0
- package/lib/commands/lang.js +266 -0
- package/lib/utils/browser.js +125 -0
- package/lib/utils/config.js +361 -0
- package/lib/utils/git.js +44 -0
- package/lib/utils/lang-file-manager.js +151 -0
- package/lib/utils/proxy-manager.js +364 -0
- package/lib/utils/translation.js +199 -0
- package/package.json +35 -0
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
const axios = require('axios');
|
|
2
|
+
const cheerio = require('cheerio');
|
|
3
|
+
const { HttpProxyAgent } = require('http-proxy-agent');
|
|
4
|
+
const chalk = require('chalk');
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const os = require('os');
|
|
8
|
+
const { ConfigManager } = require('./config');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* 代理管理器类
|
|
12
|
+
* 从 free-proxy-list.net 获取代理列表并实现轮换
|
|
13
|
+
*/
|
|
14
|
+
class ProxyManager {
|
|
15
|
+
constructor() {
|
|
16
|
+
this.proxies = [];
|
|
17
|
+
this.currentIndex = 0;
|
|
18
|
+
this.failedProxies = new Set();
|
|
19
|
+
this.lastFetchTime = 0;
|
|
20
|
+
this.fetchInterval = 30 * 60 * 1000; // 30分钟更新一次
|
|
21
|
+
|
|
22
|
+
// 缓存文件路径
|
|
23
|
+
this.cacheDir = path.join(os.homedir(), '.daodou');
|
|
24
|
+
this.cacheFile = path.join(this.cacheDir, 'proxy-cache.json');
|
|
25
|
+
this.failedProxiesFile = path.join(this.cacheDir, 'failed-proxies.json');
|
|
26
|
+
|
|
27
|
+
// 配置管理器
|
|
28
|
+
this.configManager = new ConfigManager();
|
|
29
|
+
this.config = this.configManager.getLangConfig();
|
|
30
|
+
|
|
31
|
+
// 确保缓存目录存在
|
|
32
|
+
this.ensureCacheDir();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* 获取代理列表URL
|
|
37
|
+
*/
|
|
38
|
+
getProxyListUrl() {
|
|
39
|
+
return this.config?.proxyListUrl || 'https://free-proxy-list.net/';
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* 获取代理测试URL
|
|
44
|
+
*/
|
|
45
|
+
getProxyTestUrl() {
|
|
46
|
+
return this.config?.proxyTestUrl || 'https://httpbin.org/ip';
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* 确保缓存目录存在
|
|
51
|
+
*/
|
|
52
|
+
ensureCacheDir() {
|
|
53
|
+
if (!fs.existsSync(this.cacheDir)) {
|
|
54
|
+
fs.mkdirSync(this.cacheDir, { recursive: true });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* 从缓存文件加载代理列表
|
|
60
|
+
* @returns {Promise<Array>} 代理列表
|
|
61
|
+
*/
|
|
62
|
+
async loadFromCache() {
|
|
63
|
+
try {
|
|
64
|
+
if (!fs.existsSync(this.cacheFile)) {
|
|
65
|
+
return [];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const cacheData = JSON.parse(fs.readFileSync(this.cacheFile, 'utf8'));
|
|
69
|
+
const now = Date.now();
|
|
70
|
+
|
|
71
|
+
// 检查缓存是否过期
|
|
72
|
+
if (now - cacheData.timestamp > this.fetchInterval) {
|
|
73
|
+
console.log(chalk.yellow('⚠️ 代理缓存已过期,需要重新获取'));
|
|
74
|
+
return [];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
console.log(chalk.blue(`📁 从缓存加载 ${cacheData.proxies.length} 个代理服务器`));
|
|
78
|
+
return cacheData.proxies || [];
|
|
79
|
+
} catch (error) {
|
|
80
|
+
console.log(chalk.red(`❌ 加载代理缓存失败: ${error.message}`));
|
|
81
|
+
return [];
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* 保存代理列表到缓存文件
|
|
87
|
+
* @param {Array} proxies 代理列表
|
|
88
|
+
*/
|
|
89
|
+
async saveToCache(proxies) {
|
|
90
|
+
try {
|
|
91
|
+
const cacheData = {
|
|
92
|
+
timestamp: Date.now(),
|
|
93
|
+
proxies: proxies
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
fs.writeFileSync(this.cacheFile, JSON.stringify(cacheData, null, 2));
|
|
97
|
+
console.log(chalk.green(`💾 已缓存 ${proxies.length} 个代理服务器到 ${this.cacheFile}`));
|
|
98
|
+
} catch (error) {
|
|
99
|
+
console.log(chalk.red(`❌ 保存代理缓存失败: ${error.message}`));
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* 从缓存文件加载失败的代理列表
|
|
105
|
+
*/
|
|
106
|
+
loadFailedProxies() {
|
|
107
|
+
try {
|
|
108
|
+
if (!fs.existsSync(this.failedProxiesFile)) {
|
|
109
|
+
return new Set();
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const failedProxies = JSON.parse(fs.readFileSync(this.failedProxiesFile, 'utf8'));
|
|
113
|
+
return new Set(failedProxies || []);
|
|
114
|
+
} catch (error) {
|
|
115
|
+
console.log(chalk.red(`❌ 加载失败代理列表失败: ${error.message}`));
|
|
116
|
+
return new Set();
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* 保存失败的代理列表到缓存文件
|
|
122
|
+
*/
|
|
123
|
+
saveFailedProxies() {
|
|
124
|
+
try {
|
|
125
|
+
const failedProxies = Array.from(this.failedProxies);
|
|
126
|
+
fs.writeFileSync(this.failedProxiesFile, JSON.stringify(failedProxies, null, 2));
|
|
127
|
+
} catch (error) {
|
|
128
|
+
console.log(chalk.red(`❌ 保存失败代理列表失败: ${error.message}`));
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* 从 free-proxy-list.net 获取代理列表
|
|
134
|
+
* @returns {Promise<Array>} 代理列表
|
|
135
|
+
*/
|
|
136
|
+
async fetchProxies() {
|
|
137
|
+
try {
|
|
138
|
+
console.log(chalk.blue('🔍 正在获取代理服务器列表...'));
|
|
139
|
+
|
|
140
|
+
const response = await axios.get(this.getProxyListUrl(), {
|
|
141
|
+
timeout: 10000,
|
|
142
|
+
headers: {
|
|
143
|
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
const $ = cheerio.load(response.data);
|
|
148
|
+
const proxies = [];
|
|
149
|
+
|
|
150
|
+
// 解析表格数据
|
|
151
|
+
$('table.table-striped tbody tr').each((index, element) => {
|
|
152
|
+
const $row = $(element);
|
|
153
|
+
const cells = $row.find('td');
|
|
154
|
+
|
|
155
|
+
if (cells.length >= 8) {
|
|
156
|
+
const ip = $(cells[0]).text().trim();
|
|
157
|
+
const port = $(cells[1]).text().trim();
|
|
158
|
+
const code = $(cells[2]).text().trim();
|
|
159
|
+
const country = $(cells[3]).text().trim();
|
|
160
|
+
const anonymity = $(cells[4]).text().trim();
|
|
161
|
+
const google = $(cells[5]).text().trim();
|
|
162
|
+
const https = $(cells[6]).text().trim();
|
|
163
|
+
const lastChecked = $(cells[7]).text().trim();
|
|
164
|
+
|
|
165
|
+
// 过滤条件:Anonymity = "anonymous" 且 Google ≠ "no"
|
|
166
|
+
if (anonymity === 'anonymous' && google !== 'no') {
|
|
167
|
+
proxies.push({
|
|
168
|
+
ip,
|
|
169
|
+
port: parseInt(port),
|
|
170
|
+
code,
|
|
171
|
+
country,
|
|
172
|
+
anonymity,
|
|
173
|
+
google,
|
|
174
|
+
https: https === 'yes',
|
|
175
|
+
lastChecked,
|
|
176
|
+
url: `http://${ip}:${port}`
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
console.log(chalk.green(`✅ 获取到 ${proxies.length} 个符合条件的代理服务器`));
|
|
183
|
+
|
|
184
|
+
// 保存到缓存
|
|
185
|
+
await this.saveToCache(proxies);
|
|
186
|
+
|
|
187
|
+
return proxies;
|
|
188
|
+
|
|
189
|
+
} catch (error) {
|
|
190
|
+
console.log(chalk.red(`❌ 获取代理列表失败: ${error.message}`));
|
|
191
|
+
return [];
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* 获取可用的代理列表
|
|
197
|
+
* @returns {Promise<Array>} 可用的代理列表
|
|
198
|
+
*/
|
|
199
|
+
async getAvailableProxies() {
|
|
200
|
+
// 如果代理列表为空,尝试从缓存加载
|
|
201
|
+
if (this.proxies.length === 0) {
|
|
202
|
+
this.proxies = await this.loadFromCache();
|
|
203
|
+
this.failedProxies = this.loadFailedProxies();
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// 如果缓存中没有代理或缓存为空,重新获取
|
|
207
|
+
if (this.proxies.length === 0) {
|
|
208
|
+
this.proxies = await this.fetchProxies();
|
|
209
|
+
this.failedProxies.clear();
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// 过滤掉失败的代理
|
|
213
|
+
const availableProxies = this.proxies.filter(proxy =>
|
|
214
|
+
!this.failedProxies.has(proxy.url)
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
return availableProxies;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* 获取下一个可用的代理
|
|
222
|
+
* @returns {Promise<Object|null>} 代理对象或null
|
|
223
|
+
*/
|
|
224
|
+
async getNextProxy() {
|
|
225
|
+
const availableProxies = await this.getAvailableProxies();
|
|
226
|
+
|
|
227
|
+
if (availableProxies.length === 0) {
|
|
228
|
+
console.log(chalk.yellow('⚠️ 没有可用的代理服务器'));
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// 轮换选择代理
|
|
233
|
+
const proxy = availableProxies[this.currentIndex % availableProxies.length];
|
|
234
|
+
this.currentIndex++;
|
|
235
|
+
|
|
236
|
+
return proxy;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* 标记代理为失败
|
|
241
|
+
* @param {string} proxyUrl 代理URL
|
|
242
|
+
*/
|
|
243
|
+
markProxyFailed(proxyUrl) {
|
|
244
|
+
this.failedProxies.add(proxyUrl);
|
|
245
|
+
|
|
246
|
+
// 从代理列表中移除失败的代理
|
|
247
|
+
this.proxies = this.proxies.filter(proxy => proxy.url !== proxyUrl);
|
|
248
|
+
|
|
249
|
+
// 保存失败代理列表和更新后的代理列表
|
|
250
|
+
this.saveFailedProxies();
|
|
251
|
+
this.saveToCache(this.proxies);
|
|
252
|
+
|
|
253
|
+
console.log(chalk.yellow(`⚠️ 代理 ${proxyUrl} 已从列表中移除`));
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* 创建代理代理
|
|
258
|
+
* @param {Object} proxy 代理对象
|
|
259
|
+
* @returns {HttpProxyAgent} 代理代理
|
|
260
|
+
*/
|
|
261
|
+
createProxyAgent(proxy) {
|
|
262
|
+
if (!proxy) return null;
|
|
263
|
+
|
|
264
|
+
try {
|
|
265
|
+
return new HttpProxyAgent(proxy.url);
|
|
266
|
+
} catch (error) {
|
|
267
|
+
console.log(chalk.red(`❌ 创建代理代理失败: ${error.message}`));
|
|
268
|
+
return null;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* 测试代理是否可用
|
|
274
|
+
* @param {Object} proxy 代理对象
|
|
275
|
+
* @returns {Promise<boolean>} 是否可用
|
|
276
|
+
*/
|
|
277
|
+
async testProxy(proxy) {
|
|
278
|
+
if (!proxy) return false;
|
|
279
|
+
|
|
280
|
+
try {
|
|
281
|
+
const agent = this.createProxyAgent(proxy);
|
|
282
|
+
if (!agent) return false;
|
|
283
|
+
|
|
284
|
+
const response = await axios.get(this.getProxyTestUrl(), {
|
|
285
|
+
httpAgent: agent,
|
|
286
|
+
httpsAgent: agent,
|
|
287
|
+
timeout: 5000
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
return response.status === 200;
|
|
291
|
+
} catch (error) {
|
|
292
|
+
return false;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* 获取统计信息
|
|
298
|
+
* @returns {Object} 统计信息
|
|
299
|
+
*/
|
|
300
|
+
getStats() {
|
|
301
|
+
return {
|
|
302
|
+
totalProxies: this.proxies.length,
|
|
303
|
+
failedProxies: this.failedProxies.size,
|
|
304
|
+
availableProxies: this.proxies.length - this.failedProxies.size,
|
|
305
|
+
currentIndex: this.currentIndex,
|
|
306
|
+
cacheFile: this.cacheFile,
|
|
307
|
+
failedProxiesFile: this.failedProxiesFile
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* 清理缓存文件
|
|
313
|
+
*/
|
|
314
|
+
clearCache() {
|
|
315
|
+
try {
|
|
316
|
+
if (fs.existsSync(this.cacheFile)) {
|
|
317
|
+
fs.unlinkSync(this.cacheFile);
|
|
318
|
+
console.log(chalk.green(`🗑️ 已清理代理缓存文件: ${this.cacheFile}`));
|
|
319
|
+
}
|
|
320
|
+
if (fs.existsSync(this.failedProxiesFile)) {
|
|
321
|
+
fs.unlinkSync(this.failedProxiesFile);
|
|
322
|
+
console.log(chalk.green(`🗑️ 已清理失败代理缓存文件: ${this.failedProxiesFile}`));
|
|
323
|
+
}
|
|
324
|
+
} catch (error) {
|
|
325
|
+
console.log(chalk.red(`❌ 清理缓存失败: ${error.message}`));
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* 强制刷新代理列表(忽略缓存)
|
|
331
|
+
* @returns {Promise<Array>} 新的代理列表
|
|
332
|
+
*/
|
|
333
|
+
async forceRefresh() {
|
|
334
|
+
console.log(chalk.blue('🔄 强制刷新代理列表...'));
|
|
335
|
+
this.proxies = await this.fetchProxies();
|
|
336
|
+
this.failedProxies.clear();
|
|
337
|
+
this.currentIndex = 0;
|
|
338
|
+
return this.proxies;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* 检查是否还有可用代理
|
|
343
|
+
* @returns {boolean} 是否还有可用代理
|
|
344
|
+
*/
|
|
345
|
+
hasAvailableProxies() {
|
|
346
|
+
const availableProxies = this.proxies.filter(proxy =>
|
|
347
|
+
!this.failedProxies.has(proxy.url)
|
|
348
|
+
);
|
|
349
|
+
return availableProxies.length > 0;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* 获取可用代理数量
|
|
354
|
+
* @returns {number} 可用代理数量
|
|
355
|
+
*/
|
|
356
|
+
getAvailableProxyCount() {
|
|
357
|
+
const availableProxies = this.proxies.filter(proxy =>
|
|
358
|
+
!this.failedProxies.has(proxy.url)
|
|
359
|
+
);
|
|
360
|
+
return availableProxies.length;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
module.exports = ProxyManager;
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
const { translate } = require('@vitalets/google-translate-api');
|
|
2
|
+
const chalk = require('chalk');
|
|
3
|
+
const ProxyManager = require('./proxy-manager');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 翻译服务类
|
|
7
|
+
* 处理 Google Translate API 调用和重试机制
|
|
8
|
+
*/
|
|
9
|
+
class TranslationService {
|
|
10
|
+
constructor() {
|
|
11
|
+
this.maxRetries = 3;
|
|
12
|
+
this.retryDelay = 1000; // 1秒
|
|
13
|
+
this.proxyManager = new ProxyManager();
|
|
14
|
+
this.currentProxy = null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* 翻译文本(支持多代理轮换)
|
|
19
|
+
* @param {string} text - 要翻译的文本
|
|
20
|
+
* @param {string} targetLang - 目标语言代码
|
|
21
|
+
* @param {string} sourceLang - 源语言代码,默认为 'en'
|
|
22
|
+
* @returns {Promise<{success: boolean, result: string, error: string}>}
|
|
23
|
+
*/
|
|
24
|
+
async translateText(text, targetLang, sourceLang = 'en') {
|
|
25
|
+
console.log(chalk.blue(` 🌐 步骤 2/4: 开始翻译 "${text}"...`));
|
|
26
|
+
|
|
27
|
+
// 首先尝试直连(不使用代理)
|
|
28
|
+
console.log(chalk.gray(` 🔗 尝试直连翻译...`));
|
|
29
|
+
try {
|
|
30
|
+
const result = await translate(text, {
|
|
31
|
+
from: sourceLang,
|
|
32
|
+
to: targetLang
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
console.log(chalk.green(` 🔄 步骤 3/4: 翻译完成 "${text}" -> "${result.text}"`));
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
success: true,
|
|
39
|
+
result: result.text,
|
|
40
|
+
error: null
|
|
41
|
+
};
|
|
42
|
+
} catch (error) {
|
|
43
|
+
console.log(chalk.yellow(` ⚠️ 直连失败: ${error.message}`));
|
|
44
|
+
console.log(chalk.blue(` 🔄 开始使用代理轮换...`));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// 直连失败,开始代理轮换
|
|
48
|
+
const availableProxies = await this.proxyManager.getAvailableProxies();
|
|
49
|
+
|
|
50
|
+
if (availableProxies.length === 0) {
|
|
51
|
+
console.log(chalk.red(` ❌ 没有可用的代理服务器`));
|
|
52
|
+
return {
|
|
53
|
+
success: false,
|
|
54
|
+
result: null,
|
|
55
|
+
error: '没有可用的代理服务器'
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// 轮询所有可用代理
|
|
60
|
+
let proxyIndex = 0;
|
|
61
|
+
while (proxyIndex < availableProxies.length) {
|
|
62
|
+
const proxy = availableProxies[proxyIndex];
|
|
63
|
+
const remainingProxies = this.proxyManager.getAvailableProxyCount();
|
|
64
|
+
|
|
65
|
+
console.log(chalk.gray(` 🔗 使用代理: ${proxy.ip}:${proxy.port} (${proxy.country}) [${proxyIndex + 1}/${availableProxies.length}] (剩余: ${remainingProxies})`));
|
|
66
|
+
|
|
67
|
+
// 对每个代理尝试3次
|
|
68
|
+
let proxySuccess = false;
|
|
69
|
+
for (let attempt = 1; attempt <= this.maxRetries; attempt++) {
|
|
70
|
+
try {
|
|
71
|
+
const agent = this.proxyManager.createProxyAgent(proxy);
|
|
72
|
+
if (!agent) {
|
|
73
|
+
console.log(chalk.red(` ❌ 创建代理代理失败`));
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const result = await translate(text, {
|
|
78
|
+
from: sourceLang,
|
|
79
|
+
to: targetLang,
|
|
80
|
+
fetchOptions: { agent }
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
console.log(chalk.green(` 🔄 步骤 3/4: 翻译完成 "${text}" -> "${result.text}"`));
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
success: true,
|
|
87
|
+
result: result.text,
|
|
88
|
+
error: null
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
} catch (error) {
|
|
92
|
+
console.log(chalk.red(` ❌ 代理 ${proxy.ip}:${proxy.port} 失败 (${attempt}/${this.maxRetries}): ${error.message}`));
|
|
93
|
+
|
|
94
|
+
if (attempt < this.maxRetries) {
|
|
95
|
+
await this.delay(this.retryDelay * attempt);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// 如果这个代理3次都失败了,标记为失败并从列表中移除
|
|
101
|
+
if (!proxySuccess) {
|
|
102
|
+
console.log(chalk.yellow(` ⚠️ 代理 ${proxy.ip}:${proxy.port} 已失败3次,从列表中移除`));
|
|
103
|
+
this.proxyManager.markProxyFailed(proxy.url);
|
|
104
|
+
|
|
105
|
+
// 检查是否还有可用代理
|
|
106
|
+
if (!this.proxyManager.hasAvailableProxies()) {
|
|
107
|
+
console.log(chalk.red(` ❌ 所有代理都已失败,无法继续翻译`));
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// 重新获取可用代理列表(因为可能有代理被移除)
|
|
112
|
+
const newAvailableProxies = await this.proxyManager.getAvailableProxies();
|
|
113
|
+
if (newAvailableProxies.length === 0) {
|
|
114
|
+
console.log(chalk.red(` ❌ 没有可用的代理服务器`));
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// 更新可用代理列表
|
|
119
|
+
availableProxies.length = 0;
|
|
120
|
+
availableProxies.push(...newAvailableProxies);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
proxyIndex++;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// 所有代理都失败了
|
|
127
|
+
console.log(chalk.red(` ❌ 所有代理都失败了,跳过该语言`));
|
|
128
|
+
return {
|
|
129
|
+
success: false,
|
|
130
|
+
result: null,
|
|
131
|
+
error: '所有代理都失败了'
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* 延迟函数
|
|
137
|
+
* @param {number} ms - 延迟毫秒数
|
|
138
|
+
*/
|
|
139
|
+
delay(ms) {
|
|
140
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* 检查是否为默认语言
|
|
145
|
+
* @param {string} lang - 语言代码
|
|
146
|
+
* @param {string} defaultLang - 默认语言代码
|
|
147
|
+
* @returns {boolean}
|
|
148
|
+
*/
|
|
149
|
+
isDefaultLanguage(lang, defaultLang) {
|
|
150
|
+
return lang === defaultLang;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* 获取语言显示名称
|
|
155
|
+
* @param {string} langCode - 语言代码
|
|
156
|
+
* @returns {string}
|
|
157
|
+
*/
|
|
158
|
+
getLanguageDisplayName(langCode) {
|
|
159
|
+
const languageNames = {
|
|
160
|
+
'en': 'English',
|
|
161
|
+
'zh': '中文',
|
|
162
|
+
'ja': '日本語',
|
|
163
|
+
'fr': 'Français',
|
|
164
|
+
'de': 'Deutsch',
|
|
165
|
+
'es': 'Español',
|
|
166
|
+
'it': 'Italiano',
|
|
167
|
+
'pt': 'Português',
|
|
168
|
+
'ru': 'Русский',
|
|
169
|
+
'ko': '한국어',
|
|
170
|
+
'ar': 'العربية',
|
|
171
|
+
'hi': 'हिन्दी'
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
return languageNames[langCode] || langCode.toUpperCase();
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* 获取代理统计信息
|
|
179
|
+
* @returns {Object} 统计信息
|
|
180
|
+
*/
|
|
181
|
+
getProxyStats() {
|
|
182
|
+
return this.proxyManager.getStats();
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* 显示代理统计信息
|
|
187
|
+
*/
|
|
188
|
+
showProxyStats() {
|
|
189
|
+
const stats = this.getProxyStats();
|
|
190
|
+
console.log(chalk.blue('\n📊 代理服务器统计:'));
|
|
191
|
+
console.log(chalk.gray(` 总代理数: ${stats.totalProxies}`));
|
|
192
|
+
console.log(chalk.green(` 可用代理: ${stats.availableProxies}`));
|
|
193
|
+
console.log(chalk.red(` 失败代理: ${stats.failedProxies}`));
|
|
194
|
+
console.log(chalk.gray(` 缓存文件: ${stats.cacheFile}`));
|
|
195
|
+
console.log(chalk.gray(` 失败代理文件: ${stats.failedProxiesFile}`));
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
module.exports = TranslationService;
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "daodou-command",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "刀豆命令行工具 - 自动化构建和部署",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"dao": "bin/daodou.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"start": "node ./bin/daodou.js",
|
|
11
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"刀豆"
|
|
15
|
+
],
|
|
16
|
+
"author": "刀豆团队",
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@vitalets/google-translate-api": "^9.2.1",
|
|
20
|
+
"axios": "^1.12.0",
|
|
21
|
+
"chalk": "^4.1.2",
|
|
22
|
+
"cheerio": "^1.1.2",
|
|
23
|
+
"commander": "^11.1.0",
|
|
24
|
+
"http-proxy-agent": "^7.0.2",
|
|
25
|
+
"inquirer": "^8.2.6",
|
|
26
|
+
"json5": "^2.2.3",
|
|
27
|
+
"ora": "^5.4.1",
|
|
28
|
+
"puppeteer": "^24.12.1",
|
|
29
|
+
"simple-git": "^3.20.0",
|
|
30
|
+
"ws": "^8.14.2"
|
|
31
|
+
},
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=14.0.0"
|
|
34
|
+
}
|
|
35
|
+
}
|