|
@@ -8,6 +8,9 @@ class BotManager {
|
|
|
this.botToken = process.env.TELEGRAM_BOT_TOKEN;
|
|
|
this.allowedChatIds = process.env.TELEGRAM_CHAT_ID?.split(',').map(id => id.trim()) || [];
|
|
|
this.subscriptionManager = new MultiSubscriptionManager();
|
|
|
+ this.isReconnecting = false;
|
|
|
+ this.reconnectAttempts = 0;
|
|
|
+ this.maxReconnectAttempts = 5;
|
|
|
|
|
|
// 可选的代理配置
|
|
|
this.proxyConfig = null;
|
|
@@ -25,6 +28,14 @@ class BotManager {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+ this.initializeBot();
|
|
|
+ this.speedTestMode = 'concurrent'; // 默认测速模式
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 初始化Telegram Bot
|
|
|
+ */
|
|
|
+ initializeBot() {
|
|
|
try {
|
|
|
const botOptions = {
|
|
|
polling: true,
|
|
@@ -46,31 +57,43 @@ class BotManager {
|
|
|
|
|
|
this.bot = new TelegramBot(this.botToken, botOptions);
|
|
|
|
|
|
- // 添加错误处理
|
|
|
+ // 改进的错误处理
|
|
|
this.bot.on('polling_error', (error) => {
|
|
|
logger.warn('Telegram轮询错误,尝试重连...', {
|
|
|
error: error.message,
|
|
|
code: error.code
|
|
|
});
|
|
|
|
|
|
- // 如果是网络超时,尝试重新启动轮询
|
|
|
- if (error.code === 'EFATAL' || error.message.includes('ESOCKETTIMEDOUT')) {
|
|
|
- setTimeout(() => {
|
|
|
- try {
|
|
|
- this.bot.stopPolling();
|
|
|
- setTimeout(() => {
|
|
|
- this.bot.startPolling().catch(reconnectError => {
|
|
|
- logger.error('Telegram机器人重连失败', { error: reconnectError.message });
|
|
|
- });
|
|
|
- logger.info('Telegram机器人重连成功');
|
|
|
- }, 5000);
|
|
|
- } catch (reconnectError) {
|
|
|
- logger.error('Telegram机器人重连失败', { error: reconnectError.message });
|
|
|
- }
|
|
|
- }, 10000);
|
|
|
+ // 处理各种网络错误
|
|
|
+ const isNetworkError = error.code === 'EFATAL' ||
|
|
|
+ error.code === 'ESOCKETTIMEDOUT' ||
|
|
|
+ error.code === 'ECONNRESET' ||
|
|
|
+ error.code === 'ENOTFOUND' ||
|
|
|
+ error.message.includes('timeout') ||
|
|
|
+ error.message.includes('network') ||
|
|
|
+ error.message.includes('AggregateError');
|
|
|
+
|
|
|
+ if (isNetworkError) {
|
|
|
+ this.handleReconnection();
|
|
|
}
|
|
|
});
|
|
|
|
|
|
+ // 添加webhook错误处理
|
|
|
+ this.bot.on('webhook_error', (error) => {
|
|
|
+ logger.warn('Telegram Webhook错误', {
|
|
|
+ error: error.message,
|
|
|
+ code: error.code
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ // 添加错误处理
|
|
|
+ this.bot.on('error', (error) => {
|
|
|
+ logger.error('Telegram Bot错误', {
|
|
|
+ error: error.message,
|
|
|
+ code: error.code
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
this.enabled = true;
|
|
|
this.setupCommands();
|
|
|
this.setupMessageHandlers();
|
|
@@ -81,6 +104,71 @@ class BotManager {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 处理重连逻辑
|
|
|
+ */
|
|
|
+ handleReconnection() {
|
|
|
+ // 防止重复重连
|
|
|
+ if (this.isReconnecting) {
|
|
|
+ logger.debug('Telegram重连正在进行中,跳过本次重连');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ this.isReconnecting = true;
|
|
|
+ this.reconnectAttempts++;
|
|
|
+
|
|
|
+ if (this.reconnectAttempts > this.maxReconnectAttempts) {
|
|
|
+ logger.error('Telegram重连次数超过限制,停止重连', {
|
|
|
+ attempts: this.reconnectAttempts,
|
|
|
+ maxAttempts: this.maxReconnectAttempts
|
|
|
+ });
|
|
|
+ this.isReconnecting = false;
|
|
|
+ this.reconnectAttempts = 0;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const delay = Math.min(10000 * this.reconnectAttempts, 60000); // 递增延迟,最大60秒
|
|
|
+
|
|
|
+ setTimeout(async () => {
|
|
|
+ try {
|
|
|
+ logger.info(`尝试重新连接Telegram Bot... (第${this.reconnectAttempts}次尝试)`);
|
|
|
+
|
|
|
+ // 停止当前轮询
|
|
|
+ if (this.bot && this.bot.stopPolling) {
|
|
|
+ this.bot.stopPolling();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 等待一段时间后重新启动
|
|
|
+ setTimeout(async () => {
|
|
|
+ try {
|
|
|
+ if (this.bot && this.bot.startPolling) {
|
|
|
+ await this.bot.startPolling();
|
|
|
+ logger.info('Telegram机器人重连成功');
|
|
|
+ this.isReconnecting = false;
|
|
|
+ this.reconnectAttempts = 0; // 重置重连计数
|
|
|
+ }
|
|
|
+ } catch (reconnectError) {
|
|
|
+ logger.error('Telegram机器人重连失败', {
|
|
|
+ error: reconnectError.message,
|
|
|
+ code: reconnectError.code,
|
|
|
+ attempt: this.reconnectAttempts
|
|
|
+ });
|
|
|
+
|
|
|
+ // 如果重连失败,再次尝试
|
|
|
+ this.isReconnecting = false;
|
|
|
+ if (this.reconnectAttempts < this.maxReconnectAttempts) {
|
|
|
+ this.handleReconnection();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }, 5000);
|
|
|
+
|
|
|
+ } catch (error) {
|
|
|
+ logger.error('Telegram重连过程中发生错误', { error: error.message });
|
|
|
+ this.isReconnecting = false;
|
|
|
+ }
|
|
|
+ }, delay);
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 设置机器人命令
|
|
|
*/
|
|
@@ -95,7 +183,8 @@ class BotManager {
|
|
|
{ command: '/add_subscription', description: '添加订阅链接' },
|
|
|
{ command: '/remove_subscription', description: '删除订阅' },
|
|
|
{ command: '/update_subscriptions', description: '手动更新订阅' },
|
|
|
- { command: '/test_speed', description: '手动触发测速' }
|
|
|
+ { command: '/test_speed', description: '手动触发测速' },
|
|
|
+ { command: '/set_speed_mode', description: '设置测速模式(concurrent/serial)' }
|
|
|
]);
|
|
|
}
|
|
|
|
|
@@ -150,6 +239,11 @@ class BotManager {
|
|
|
await this.handleTestSpeed(msg);
|
|
|
});
|
|
|
|
|
|
+ // 处理 /set_speed_mode 命令
|
|
|
+ this.bot.onText(/\/set_speed_mode (.+)/, async (msg, match) => {
|
|
|
+ await this.handleSetSpeedMode(msg, match[1]);
|
|
|
+ });
|
|
|
+
|
|
|
// 处理普通消息(只处理订阅链接,不处理其他消息)
|
|
|
this.bot.on('message', async (msg) => {
|
|
|
// 只处理文本消息
|
|
@@ -468,18 +562,65 @@ class BotManager {
|
|
|
}
|
|
|
|
|
|
try {
|
|
|
- await this.sendMessage(chatId, '⚡ 开始手动测速...');
|
|
|
-
|
|
|
- // 这里需要调用测速功能
|
|
|
- // 暂时发送一个简单的消息
|
|
|
- await this.sendMessage(chatId, '✅ 测速功能开发中,请稍后...');
|
|
|
+ await this.sendMessage(chatId, `⚡ 开始手动测速...\n当前测速模式: *${this.speedTestMode === 'concurrent' ? '并发连通性' : '串行精准延迟'}*`);
|
|
|
+ // 获取所有节点(示例,实际请根据你的业务逻辑获取节点列表)
|
|
|
+ const subscriptions = await Subscription.findAll({ where: { isActive: true } });
|
|
|
+ let allNodes = [];
|
|
|
+ for (const sub of subscriptions) {
|
|
|
+ if (sub.nodes) {
|
|
|
+ allNodes = allNodes.concat(sub.nodes);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (allNodes.length === 0) {
|
|
|
+ await this.sendMessage(chatId, '❌ 当前没有可用节点');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ let results;
|
|
|
+ if (this.speedTestMode === 'concurrent') {
|
|
|
+ // 并发连通性测试
|
|
|
+ const SpeedTester = require('./speedTester');
|
|
|
+ const speedTester = new SpeedTester();
|
|
|
+ results = await speedTester.testNodes(allNodes, { concurrency: 20 });
|
|
|
+ } else {
|
|
|
+ // 串行精准延迟测试
|
|
|
+ const SpeedTester = require('./speedTester');
|
|
|
+ const speedTester = new SpeedTester();
|
|
|
+ results = await speedTester.testNodesSerial(allNodes);
|
|
|
+ }
|
|
|
+ // 简要汇总结果
|
|
|
+ const success = results.filter(r => r.isSuccess);
|
|
|
+ const failed = results.filter(r => !r.isSuccess);
|
|
|
+ let msgText = `测速完成!\n成功: ${success.length} 个, 失败: ${failed.length} 个`;
|
|
|
+ if (success.length > 0) {
|
|
|
+ msgText += `\n前5个成功节点: ` + success.slice(0, 5).map(r => `${r.nodeName || r.nodeId}(${r.latency}ms)`).join(', ');
|
|
|
+ }
|
|
|
+ if (failed.length > 0) {
|
|
|
+ msgText += `\n前5个失败节点: ` + failed.slice(0, 5).map(r => r.nodeName || r.nodeId).join(', ');
|
|
|
+ }
|
|
|
+ await this.sendMessage(chatId, msgText);
|
|
|
} catch (error) {
|
|
|
logger.error('手动测速失败', { error: error.message });
|
|
|
await this.sendMessage(chatId, '❌ 测速失败:' + error.message);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-
|
|
|
+ /**
|
|
|
+ * 处理测速模式设置命令
|
|
|
+ */
|
|
|
+ async handleSetSpeedMode(msg, mode) {
|
|
|
+ const chatId = msg.chat.id;
|
|
|
+ if (!this.isAuthorized(chatId)) {
|
|
|
+ await this.sendMessage(chatId, '❌ 您没有权限使用此机器人');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const validModes = ['concurrent', 'serial'];
|
|
|
+ if (!validModes.includes(mode)) {
|
|
|
+ await this.sendMessage(chatId, '❌ 无效的测速模式。可选: concurrent(并发连通性), serial(串行精准延迟)');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this.speedTestMode = mode;
|
|
|
+ await this.sendMessage(chatId, `✅ 已切换测速模式为: *${mode === 'concurrent' ? '并发连通性' : '串行精准延迟'}*`);
|
|
|
+ }
|
|
|
|
|
|
/**
|
|
|
* 检查是否是订阅链接
|