Quellcode durchsuchen

添加订阅链接名字,测速方式选择按钮

Taio_O vor 1 Woche
Ursprung
Commit
087ceadf58
1 geänderte Dateien mit 140 neuen und 116 gelöschten Zeilen
  1. 140 116
      src/core/botManager.js

+ 140 - 116
src/core/botManager.js

@@ -30,6 +30,7 @@ class BotManager {
 
     this.initializeBot();
     this.speedTestMode = 'concurrent'; // 默认测速模式
+    this.pendingAddSubscription = {}; // 新增:记录每个chatId的添加订阅状态
   }
 
   /**
@@ -183,8 +184,7 @@ class BotManager {
       { command: '/add_subscription', description: '添加订阅链接' },
       { command: '/remove_subscription', description: '删除订阅' },
       { command: '/update_subscriptions', description: '手动更新订阅' },
-      { command: '/test_speed', description: '手动触发测速' },
-      { command: '/set_speed_mode', description: '设置测速模式(concurrent/serial)' }
+      { command: '/speed', description: '测速(弹出模式选择按钮)' }
     ]);
   }
 
@@ -234,14 +234,16 @@ class BotManager {
       await this.handleUpdateSubscriptions(msg);
     });
 
-    // 处理 /test_speed 命令
-    this.bot.onText(/\/test_speed/, async (msg) => {
-      await this.handleTestSpeed(msg);
+    // 处理 /speed 命令
+    this.bot.onText(/\/speed/, async (msg) => {
+      await this.handleSpeed(msg);
     });
-
-    // 处理 /set_speed_mode 命令
-    this.bot.onText(/\/set_speed_mode (.+)/, async (msg, match) => {
-      await this.handleSetSpeedMode(msg, match[1]);
+    // 处理测速模式按钮回调
+    this.bot.on('callback_query', async (callbackQuery) => {
+      const data = callbackQuery.data;
+      if (data.startsWith('speed_mode_concurrent_') || data.startsWith('speed_mode_serial_')) {
+        await this.handleSpeedModeButton(callbackQuery, data);
+      }
     });
 
     // 处理普通消息(只处理订阅链接,不处理其他消息)
@@ -252,9 +254,32 @@ class BotManager {
       // 如果是命令,不处理(由命令处理器处理)
       if (msg.text.startsWith('/')) return;
       
-      // 只处理订阅链接
+      const chatId = msg.chat.id;
+      // 新增:多步添加订阅流程
+      if (this.pendingAddSubscription[chatId]) {
+        const state = this.pendingAddSubscription[chatId];
+        if (state.step === 1) {
+          // 用户发送了名字,进入下一步
+          state.name = msg.text.trim();
+          state.step = 2;
+          await this.sendMessage(chatId, `✅ 名字已记录:*${state.name}*\n\n请发送订阅链接(支持 Clash、SS、Vmess、Trojan)`);
+          return;
+        } else if (state.step === 2) {
+          // 用户发送了链接,完成添加
+          const url = msg.text.trim();
+          if (!this.isSubscriptionUrl(url)) {
+            await this.sendMessage(chatId, '❌ 链接格式不正确,请重新发送订阅链接');
+            return;
+          }
+          const name = state.name;
+          delete this.pendingAddSubscription[chatId];
+          await this.addSubscriptionWithName(chatId, name, url);
+          return;
+        }
+      }
+      // 原有逻辑:只处理订阅链接
       if (this.isSubscriptionUrl(msg.text)) {
-        await this.addSubscription(msg.chat.id, msg.text);
+        await this.addSubscription(chatId, msg.text);
       }
       // 其他消息不回复
     });
@@ -305,8 +330,10 @@ class BotManager {
 • /update_subscriptions - 手动更新订阅
 • /test_speed - 手动触发测速
 
+💡 *添加订阅方式:*
+• 发送 /add_subscription 按提示分步添加(先名字,后链接)
+
 💡 *使用提示:*
-• 直接发送订阅链接即可添加订阅
 • 使用 /remove_subscription 查看订阅列表
 • 使用 /delete_1 删除第1个订阅
 • 支持多种格式:Clash、Shadowsocks、Vmess等`;
@@ -459,15 +486,14 @@ class BotManager {
       await this.sendMessage(chatId, '❌ 您没有权限使用此机器人');
       return;
     }
-
+    // 新增:进入等待名字状态
+    this.pendingAddSubscription[chatId] = { step: 1 };
     await this.sendMessage(chatId, 
       `📥 *添加订阅*\n\n` +
-      `请发送订阅链接,支持以下格式:\n` +
-      `• Clash配置:\`https://example.com/clash.yaml\`\n` +
-      `• Shadowsocks:\`ss://...\`\n` +
-      `• Vmess:\`vmess://...\`\n` +
-      `• Trojan:\`trojan://...\`\n\n` +
-      `💡 直接发送链接即可添加订阅`
+      `请按照以下步骤添加订阅:\n\n` +
+      `1. 现在发送订阅名字\n` +
+      `2. 然后发送订阅链接\n\n` +
+      `💡 支持格式:Clash、Shadowsocks、Vmess、Trojan`
     );
   }
 
@@ -622,6 +648,71 @@ class BotManager {
     await this.sendMessage(chatId, `✅ 已切换测速模式为: *${mode === 'concurrent' ? '并发连通性' : '串行精准延迟'}*`);
   }
 
+  /**
+   * 处理 /speed 命令,弹出所有订阅列表,每个订阅后有“并发测速/精准测速”按钮,显示当前模式。
+   */
+  async handleSpeed(msg) {
+    const chatId = msg.chat.id;
+    if (!this.isAuthorized(chatId)) {
+      await this.sendMessage(chatId, '❌ 您没有权限使用此机器人');
+      return;
+    }
+    const { Subscription } = require('../models');
+    const subscriptions = await Subscription.findAll({ where: { isActive: true } });
+    if (subscriptions.length === 0) {
+      await this.sendMessage(chatId, '📭 暂无订阅,请先添加订阅');
+      return;
+    }
+    // 构建消息文本和按钮
+    let text = '请选择要设置测速模式的订阅:\n';
+    const inline_keyboard = [];
+    subscriptions.forEach((sub, idx) => {
+      let mode = 'concurrent';
+      if (sub.speedTestConfig && typeof sub.speedTestConfig === 'object' && sub.speedTestConfig.mode) {
+        mode = sub.speedTestConfig.mode;
+      }
+      const modeText = mode === 'concurrent' ? '并发测速' : '精准测速';
+      text += `\n*${sub.name || sub.id}*  当前: *${modeText}*`;
+      inline_keyboard.push([
+        { text: `🚀 并发测速${mode === 'concurrent' ? '(当前)' : ''}`, callback_data: `speed_mode_concurrent_${sub.id}` },
+        { text: `🎯 精准测速${mode === 'serial' ? '(当前)' : ''}`, callback_data: `speed_mode_serial_${sub.id}` }
+      ]);
+    });
+    const opts = {
+      reply_markup: { inline_keyboard },
+      parse_mode: 'Markdown'
+    };
+    await this.sendMessage(chatId, text, opts);
+  }
+
+  /**
+   * 处理测速模式按钮点击,切换模式并立即测速
+   */
+  async handleSpeedModeButton(callbackQuery, data) {
+    const chatId = callbackQuery.message.chat.id;
+    // 解析模式和订阅ID
+    const match = data.match(/^speed_mode_(concurrent|serial)_(\d+)$/);
+    if (!match) return;
+    const mode = match[1];
+    const subId = parseInt(match[2]);
+    const { Subscription } = require('../models');
+    const sub = await Subscription.findByPk(subId);
+    if (!sub) {
+      await this.sendMessage(chatId, '❌ 订阅不存在');
+      return;
+    }
+    // 更新 speedTestConfig
+    let config = sub.speedTestConfig && typeof sub.speedTestConfig === 'object' ? { ...sub.speedTestConfig } : {};
+    config.mode = mode;
+    await sub.update({ speedTestConfig: config });
+    const modeText = mode === 'concurrent' ? '并发测速' : '精准测速';
+    await this.bot.editMessageText(`已切换【${sub.name || sub.id}】的测速模式为:*${modeText}*`, {
+      chat_id: chatId,
+      message_id: callbackQuery.message.message_id,
+      parse_mode: 'Markdown'
+    });
+  }
+
   /**
    * 检查是否是订阅链接
    */
@@ -635,7 +726,6 @@ class BotManager {
   async addSubscription(chatId, url) {
     try {
       await this.sendMessage(chatId, '📥 正在添加订阅...');
-      
       // 创建新订阅
       const subscription = await Subscription.create({
         name: `订阅_${Date.now()}`,
@@ -643,130 +733,64 @@ class BotManager {
         isActive: true,
         nodeCount: 0
       });
-
       // 更新订阅
       const result = await this.subscriptionManager.manualUpdateSubscription(subscription.id);
-      
+      // manualUpdateSubscription后,重新查一次数据库,获取最新节点数
+      const updatedSub = await Subscription.findByPk(subscription.id);
+      const nodeCount = updatedSub && updatedSub.nodeCount != null ? updatedSub.nodeCount : 0;
       if (result.error) {
         await this.sendMessage(chatId, `❌ 添加订阅失败:${result.error}`);
         return;
       }
-
       const message = `✅ *订阅添加成功*\n\n` +
         `📡 订阅名称:*${subscription.name}*\n` +
         `🔗 URL:\`${url}\`\n` +
-        `📊 节点数:${result.actualNodeCount || 0}\n` +
+        `📊 节点数:${nodeCount}\n` +
         `➕ 新增:${result.added || 0} 个\n` +
         `🔄 更新:${result.updated || 0} 个\n` +
         `🗑️ 移除:${result.removed || 0} 个`;
-
       await this.sendMessage(chatId, message);
     } catch (error) {
-      logger.error('添加订阅失败', { error: error.message, url });
+      logger.error('添加订阅失败', { error: error.message });
       await this.sendMessage(chatId, '❌ 添加订阅失败:' + error.message);
     }
   }
 
   /**
-   * 处理删除订阅命令
-   */
-  async handleDeleteSubscription(msg, index) {
-    const chatId = msg.chat.id;
-    
-    if (!this.isAuthorized(chatId)) {
-      await this.sendMessage(chatId, '❌ 您没有权限使用此机器人');
-      return;
-    }
-
-    try {
-      const subscriptions = await Subscription.findAll({
-        where: { isActive: true },
-        order: [['createdAt', 'DESC']]
-      });
-
-      if (index < 1 || index > subscriptions.length) {
-        await this.sendMessage(chatId, '❌ 无效的订阅编号');
-        return;
-      }
-
-      const subscription = subscriptions[index - 1];
-      
-      // 标记为非活跃
-      await subscription.update({ isActive: false });
-      
-      const message = `🗑️ *订阅删除成功*\n\n` +
-        `📡 订阅名称:*${subscription.name}*\n` +
-        `🔗 URL:\`${subscription.url}\`\n` +
-        `📊 节点数:${subscription.nodeCount || 0}`;
-
-      await this.sendMessage(chatId, message);
-    } catch (error) {
-      logger.error('删除订阅失败', { error: error.message, index });
-      await this.sendMessage(chatId, '❌ 删除订阅失败:' + error.message);
-    }
-  }
-
-  /**
-   * 根据索引删除订阅(保留兼容性)
+   * 新增:带名字的添加订阅
    */
-  async removeSubscriptionByIndex(chatId, index) {
+  async addSubscriptionWithName(chatId, name, url) {
     try {
-      const subscriptions = await Subscription.findAll({
-        where: { isActive: true },
-        order: [['createdAt', 'DESC']]
+      await this.sendMessage(chatId, '📥 正在添加订阅...');
+      // 创建新订阅
+      const subscription = await Subscription.create({
+        name: name,
+        url: url,
+        isActive: true,
+        nodeCount: 0
       });
-
-      if (index < 1 || index > subscriptions.length) {
-        await this.sendMessage(chatId, '❌ 无效的订阅编号');
+      // 更新订阅
+      const result = await this.subscriptionManager.manualUpdateSubscription(subscription.id);
+      // manualUpdateSubscription后,重新查一次数据库,获取最新节点数
+      const updatedSub = await Subscription.findByPk(subscription.id);
+      const nodeCount = updatedSub && updatedSub.nodeCount != null ? updatedSub.nodeCount : 0;
+      if (result.error) {
+        await this.sendMessage(chatId, `❌ 添加订阅失败:${result.error}`);
         return;
       }
-
-      const subscription = subscriptions[index - 1];
-      
-      // 标记为非活跃
-      await subscription.update({ isActive: false });
-      
-      const message = `🗑️ *订阅删除成功*\n\n` +
+      const message = `✅ *订阅添加成功*\n\n` +
         `📡 订阅名称:*${subscription.name}*\n` +
-        `🔗 URL:\`${subscription.url}\`\n` +
-        `📊 节点数:${subscription.nodeCount || 0}`;
-
+        `🔗 URL:\`${url}\`\n` +
+        `📊 节点数:${nodeCount}\n` +
+        `➕ 新增:${result.added || 0} 个\n` +
+        `🔄 更新:${result.updated || 0} 个\n` +
+        `🗑️ 移除:${result.removed || 0} 个`;
       await this.sendMessage(chatId, message);
     } catch (error) {
-      logger.error('删除订阅失败', { error: error.message, index });
-      await this.sendMessage(chatId, '❌ 删除订阅失败:' + error.message);
-    }
-  }
-
-  /**
-   * 获取运行时间
-   */
-  getUptime() {
-    const uptime = process.uptime();
-    const hours = Math.floor(uptime / 3600);
-    const minutes = Math.floor((uptime % 3600) / 60);
-    return `${hours}小时${minutes}分钟`;
-  }
-
-  /**
-   * 获取内存使用情况
-   */
-  getMemoryUsage() {
-    const usage = process.memoryUsage();
-    const used = Math.round(usage.heapUsed / 1024 / 1024);
-    const total = Math.round(usage.heapTotal / 1024 / 1024);
-    return `${used}MB / ${total}MB`;
-  }
-
-  /**
-   * 停止机器人
-   */
-  stop() {
-    if (this.enabled && this.bot) {
-      this.bot.stopPolling();
-      logger.info('Telegram机器人已停止');
+      logger.error('添加订阅失败', { error: error.message });
+      await this.sendMessage(chatId, '❌ 添加订阅失败:' + error.message);
     }
   }
 }
 
-module.exports = BotManager; 
+module.exports = BotManager;