botManager.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772
  1. const TelegramBot = require('node-telegram-bot-api');
  2. const logger = require('../utils/logger');
  3. const { Subscription } = require('../models');
  4. const MultiSubscriptionManager = require('./multiSubscriptionManager');
  5. class BotManager {
  6. constructor() {
  7. this.botToken = process.env.TELEGRAM_BOT_TOKEN;
  8. this.allowedChatIds = process.env.TELEGRAM_CHAT_ID?.split(',').map(id => id.trim()) || [];
  9. this.subscriptionManager = new MultiSubscriptionManager();
  10. this.isReconnecting = false;
  11. this.reconnectAttempts = 0;
  12. this.maxReconnectAttempts = 5;
  13. // 可选的代理配置
  14. this.proxyConfig = null;
  15. if (process.env.TELEGRAM_PROXY_URL) {
  16. this.proxyConfig = {
  17. host: process.env.TELEGRAM_PROXY_HOST,
  18. port: parseInt(process.env.TELEGRAM_PROXY_PORT) || 1080,
  19. protocol: process.env.TELEGRAM_PROXY_PROTOCOL || 'http'
  20. };
  21. }
  22. if (!this.botToken) {
  23. logger.warn('Telegram Bot Token未配置,机器人功能将被禁用');
  24. this.enabled = false;
  25. return;
  26. }
  27. this.initializeBot();
  28. this.speedTestMode = 'concurrent'; // 默认测速模式
  29. }
  30. /**
  31. * 初始化Telegram Bot
  32. */
  33. initializeBot() {
  34. try {
  35. const botOptions = {
  36. polling: true,
  37. request: {
  38. timeout: 30000, // 增加超时时间到30秒
  39. connect_timeout: 30000
  40. }
  41. };
  42. // 如果配置了代理,添加代理设置
  43. if (this.proxyConfig) {
  44. const { HttpsProxyAgent } = require('https-proxy-agent');
  45. const proxyUrl = `${this.proxyConfig.protocol}://${this.proxyConfig.host}:${this.proxyConfig.port}`;
  46. botOptions.request.httpsAgent = new HttpsProxyAgent(proxyUrl);
  47. logger.info(`使用代理连接Telegram: ${proxyUrl}`);
  48. } else {
  49. botOptions.request.agent = false; // 禁用代理,避免网络问题
  50. }
  51. this.bot = new TelegramBot(this.botToken, botOptions);
  52. // 改进的错误处理
  53. this.bot.on('polling_error', (error) => {
  54. logger.warn('Telegram轮询错误,尝试重连...', {
  55. error: error.message,
  56. code: error.code
  57. });
  58. // 处理各种网络错误
  59. const isNetworkError = error.code === 'EFATAL' ||
  60. error.code === 'ESOCKETTIMEDOUT' ||
  61. error.code === 'ECONNRESET' ||
  62. error.code === 'ENOTFOUND' ||
  63. error.message.includes('timeout') ||
  64. error.message.includes('network') ||
  65. error.message.includes('AggregateError');
  66. if (isNetworkError) {
  67. this.handleReconnection();
  68. }
  69. });
  70. // 添加webhook错误处理
  71. this.bot.on('webhook_error', (error) => {
  72. logger.warn('Telegram Webhook错误', {
  73. error: error.message,
  74. code: error.code
  75. });
  76. });
  77. // 添加错误处理
  78. this.bot.on('error', (error) => {
  79. logger.error('Telegram Bot错误', {
  80. error: error.message,
  81. code: error.code
  82. });
  83. });
  84. this.enabled = true;
  85. this.setupCommands();
  86. this.setupMessageHandlers();
  87. logger.info('Telegram机器人管理器初始化成功');
  88. } catch (error) {
  89. logger.error('Telegram机器人管理器初始化失败', { error: error.message });
  90. this.enabled = false;
  91. }
  92. }
  93. /**
  94. * 处理重连逻辑
  95. */
  96. handleReconnection() {
  97. // 防止重复重连
  98. if (this.isReconnecting) {
  99. logger.debug('Telegram重连正在进行中,跳过本次重连');
  100. return;
  101. }
  102. this.isReconnecting = true;
  103. this.reconnectAttempts++;
  104. if (this.reconnectAttempts > this.maxReconnectAttempts) {
  105. logger.error('Telegram重连次数超过限制,停止重连', {
  106. attempts: this.reconnectAttempts,
  107. maxAttempts: this.maxReconnectAttempts
  108. });
  109. this.isReconnecting = false;
  110. this.reconnectAttempts = 0;
  111. return;
  112. }
  113. const delay = Math.min(10000 * this.reconnectAttempts, 60000); // 递增延迟,最大60秒
  114. setTimeout(async () => {
  115. try {
  116. logger.info(`尝试重新连接Telegram Bot... (第${this.reconnectAttempts}次尝试)`);
  117. // 停止当前轮询
  118. if (this.bot && this.bot.stopPolling) {
  119. this.bot.stopPolling();
  120. }
  121. // 等待一段时间后重新启动
  122. setTimeout(async () => {
  123. try {
  124. if (this.bot && this.bot.startPolling) {
  125. await this.bot.startPolling();
  126. logger.info('Telegram机器人重连成功');
  127. this.isReconnecting = false;
  128. this.reconnectAttempts = 0; // 重置重连计数
  129. }
  130. } catch (reconnectError) {
  131. logger.error('Telegram机器人重连失败', {
  132. error: reconnectError.message,
  133. code: reconnectError.code,
  134. attempt: this.reconnectAttempts
  135. });
  136. // 如果重连失败,再次尝试
  137. this.isReconnecting = false;
  138. if (this.reconnectAttempts < this.maxReconnectAttempts) {
  139. this.handleReconnection();
  140. }
  141. }
  142. }, 5000);
  143. } catch (error) {
  144. logger.error('Telegram重连过程中发生错误', { error: error.message });
  145. this.isReconnecting = false;
  146. }
  147. }, delay);
  148. }
  149. /**
  150. * 设置机器人命令
  151. */
  152. setupCommands() {
  153. if (!this.enabled) return;
  154. this.bot.setMyCommands([
  155. { command: '/start', description: '开始使用机器人' },
  156. { command: '/help', description: '显示帮助信息' },
  157. { command: '/status', description: '查看系统状态' },
  158. { command: '/subscriptions', description: '查看所有订阅' },
  159. { command: '/add_subscription', description: '添加订阅链接' },
  160. { command: '/remove_subscription', description: '删除订阅' },
  161. { command: '/update_subscriptions', description: '手动更新订阅' },
  162. { command: '/test_speed', description: '手动触发测速' },
  163. { command: '/set_speed_mode', description: '设置测速模式(concurrent/serial)' }
  164. ]);
  165. }
  166. /**
  167. * 设置消息处理器
  168. */
  169. setupMessageHandlers() {
  170. if (!this.enabled) return;
  171. // 处理 /start 命令
  172. this.bot.onText(/\/start/, async (msg) => {
  173. await this.handleStart(msg);
  174. });
  175. // 处理 /help 命令
  176. this.bot.onText(/\/help/, async (msg) => {
  177. await this.handleHelp(msg);
  178. });
  179. // 处理 /status 命令
  180. this.bot.onText(/\/status/, async (msg) => {
  181. await this.handleStatus(msg);
  182. });
  183. // 处理 /subscriptions 命令
  184. this.bot.onText(/\/subscriptions/, async (msg) => {
  185. await this.handleSubscriptions(msg);
  186. });
  187. // 处理 /add_subscription 命令
  188. this.bot.onText(/\/add_subscription/, async (msg) => {
  189. await this.handleAddSubscription(msg);
  190. });
  191. // 处理 /remove_subscription 命令
  192. this.bot.onText(/\/remove_subscription/, async (msg) => {
  193. await this.handleRemoveSubscription(msg);
  194. });
  195. // 处理删除订阅的具体命令
  196. this.bot.onText(/\/delete_(\d+)/, async (msg, match) => {
  197. await this.handleDeleteSubscription(msg, parseInt(match[1]));
  198. });
  199. // 处理 /update_subscriptions 命令
  200. this.bot.onText(/\/update_subscriptions/, async (msg) => {
  201. await this.handleUpdateSubscriptions(msg);
  202. });
  203. // 处理 /test_speed 命令
  204. this.bot.onText(/\/test_speed/, async (msg) => {
  205. await this.handleTestSpeed(msg);
  206. });
  207. // 处理 /set_speed_mode 命令
  208. this.bot.onText(/\/set_speed_mode (.+)/, async (msg, match) => {
  209. await this.handleSetSpeedMode(msg, match[1]);
  210. });
  211. // 处理普通消息(只处理订阅链接,不处理其他消息)
  212. this.bot.on('message', async (msg) => {
  213. // 只处理文本消息
  214. if (!msg.text) return;
  215. // 如果是命令,不处理(由命令处理器处理)
  216. if (msg.text.startsWith('/')) return;
  217. // 只处理订阅链接
  218. if (this.isSubscriptionUrl(msg.text)) {
  219. await this.addSubscription(msg.chat.id, msg.text);
  220. }
  221. // 其他消息不回复
  222. });
  223. }
  224. /**
  225. * 检查用户权限
  226. */
  227. isAuthorized(chatId) {
  228. return this.allowedChatIds.includes(chatId.toString());
  229. }
  230. /**
  231. * 发送消息
  232. */
  233. async sendMessage(chatId, message, options = {}) {
  234. try {
  235. return await this.bot.sendMessage(chatId, message, {
  236. parse_mode: 'Markdown',
  237. disable_web_page_preview: true,
  238. ...options
  239. });
  240. } catch (error) {
  241. logger.error('发送Telegram消息失败', { error: error.message, chatId });
  242. throw error;
  243. }
  244. }
  245. /**
  246. * 处理 /start 命令
  247. */
  248. async handleStart(msg) {
  249. const chatId = msg.chat.id;
  250. if (!this.isAuthorized(chatId)) {
  251. await this.sendMessage(chatId, '❌ 您没有权限使用此机器人');
  252. return;
  253. }
  254. const message = `🤖 *欢迎使用测速机器人!*
  255. 📋 *可用命令:*
  256. • /help - 显示帮助信息
  257. • /status - 查看系统状态
  258. • /subscriptions - 查看所有订阅
  259. • /add_subscription - 添加订阅链接
  260. • /remove_subscription - 删除订阅
  261. • /update_subscriptions - 手动更新订阅
  262. • /test_speed - 手动触发测速
  263. 💡 *使用提示:*
  264. • 直接发送订阅链接即可添加订阅
  265. • 使用 /remove_subscription 查看订阅列表
  266. • 使用 /delete_1 删除第1个订阅
  267. • 支持多种格式:Clash、Shadowsocks、Vmess等`;
  268. await this.sendMessage(chatId, message);
  269. }
  270. /**
  271. * 处理 /help 命令
  272. */
  273. async handleHelp(msg) {
  274. const chatId = msg.chat.id;
  275. if (!this.isAuthorized(chatId)) {
  276. await this.sendMessage(chatId, '❌ 您没有权限使用此机器人');
  277. return;
  278. }
  279. const message = `📖 *帮助信息*
  280. 🔧 *订阅管理:*
  281. • 直接发送订阅链接即可添加
  282. • 支持格式:Clash、Shadowsocks、Vmess、Trojan
  283. • 系统会自动解析并去重
  284. ⚡ *测速功能:*
  285. • 自动定时测速(默认每10分钟)
  286. • 支持手动触发测速
  287. • 节点故障自动通知
  288. 📊 *状态监控:*
  289. • 实时监控节点状态
  290. • 延迟和速度测试
  291. • 故障节点自动标记
  292. 🔔 *通知功能:*
  293. • 节点故障通知
  294. • 节点恢复通知
  295. • 测试摘要报告
  296. 💡 *使用示例:*
  297. • 发送:\`https://example.com/subscription\`
  298. • 发送:\`ss://...\`
  299. • 发送:\`vmess://...\`
  300. • 删除:\`/delete_1\` 删除第1个订阅`;
  301. await this.sendMessage(chatId, message);
  302. }
  303. /**
  304. * 处理 /status 命令
  305. */
  306. async handleStatus(msg) {
  307. const chatId = msg.chat.id;
  308. if (!this.isAuthorized(chatId)) {
  309. await this.sendMessage(chatId, '❌ 您没有权限使用此机器人');
  310. return;
  311. }
  312. try {
  313. const status = await this.subscriptionManager.getStatus();
  314. const subscriptions = await Subscription.findAll({
  315. where: { isActive: true }
  316. });
  317. const totalNodes = subscriptions.reduce((sum, sub) => sum + (sub.nodeCount || 0), 0);
  318. const message = `📊 *系统状态*
  319. 📡 *订阅信息:*
  320. • 活跃订阅:${subscriptions.length} 个
  321. • 总节点数:${totalNodes} 个
  322. 🔄 *更新状态:*
  323. • 自动更新:${status.autoUpdateEnabled ? '✅ 启用' : '❌ 禁用'}
  324. • 更新间隔:${status.updateInterval || '未设置'} 秒
  325. • 最后更新:${status.lastUpdateTime || '未更新'}
  326. ⚡ *测速状态:*
  327. • 测速触发器:${status.speedTestTrigger ? '✅ 已设置' : '❌ 未设置'}
  328. • 定时测速:${process.env.ENABLE_SCHEDULED_SPEED_TEST !== 'false' ? '✅ 启用' : '❌ 禁用'}
  329. 🛠️ *系统信息:*
  330. • 运行时间:${this.getUptime()}
  331. • 内存使用:${this.getMemoryUsage()}`;
  332. await this.sendMessage(chatId, message);
  333. } catch (error) {
  334. logger.error('获取状态失败', { error: error.message });
  335. await this.sendMessage(chatId, '❌ 获取系统状态失败');
  336. }
  337. }
  338. /**
  339. * 处理 /subscriptions 命令
  340. */
  341. async handleSubscriptions(msg) {
  342. const chatId = msg.chat.id;
  343. if (!this.isAuthorized(chatId)) {
  344. await this.sendMessage(chatId, '❌ 您没有权限使用此机器人');
  345. return;
  346. }
  347. try {
  348. const subscriptions = await Subscription.findAll({
  349. where: { isActive: true },
  350. order: [['createdAt', 'ASC']]
  351. });
  352. if (subscriptions.length === 0) {
  353. await this.sendMessage(chatId, '📭 暂无订阅,请使用 /add_subscription 添加订阅');
  354. return;
  355. }
  356. let message = `📡 *订阅列表*\n\n`;
  357. subscriptions.forEach((sub, index) => {
  358. const status = sub.isActive ? '✅' : '❌';
  359. const lastUpdate = sub.lastUpdateTime
  360. ? new Date(sub.lastUpdateTime).toLocaleString('zh-CN')
  361. : '未更新';
  362. message += `${index + 1}. ${status} *${sub.name}*\n`;
  363. message += ` 📊 节点数:${sub.nodeCount || 0}\n`;
  364. message += ` 🔗 URL:\`${sub.url}\`\n`;
  365. message += ` ⏰ 最后更新:${lastUpdate}\n\n`;
  366. });
  367. message += `💡 *操作提示:*\n`;
  368. message += `• 使用 /remove_subscription 删除订阅\n`;
  369. message += `• 使用 /update_subscriptions 手动更新\n`;
  370. message += `• 直接发送新链接添加订阅`;
  371. await this.sendMessage(chatId, message);
  372. } catch (error) {
  373. logger.error('获取订阅列表失败', { error: error.message });
  374. await this.sendMessage(chatId, '❌ 获取订阅列表失败');
  375. }
  376. }
  377. /**
  378. * 处理 /add_subscription 命令
  379. */
  380. async handleAddSubscription(msg) {
  381. const chatId = msg.chat.id;
  382. if (!this.isAuthorized(chatId)) {
  383. await this.sendMessage(chatId, '❌ 您没有权限使用此机器人');
  384. return;
  385. }
  386. await this.sendMessage(chatId,
  387. `📥 *添加订阅*\n\n` +
  388. `请发送订阅链接,支持以下格式:\n` +
  389. `• Clash配置:\`https://example.com/clash.yaml\`\n` +
  390. `• Shadowsocks:\`ss://...\`\n` +
  391. `• Vmess:\`vmess://...\`\n` +
  392. `• Trojan:\`trojan://...\`\n\n` +
  393. `💡 直接发送链接即可添加订阅`
  394. );
  395. }
  396. /**
  397. * 处理 /remove_subscription 命令
  398. */
  399. async handleRemoveSubscription(msg) {
  400. const chatId = msg.chat.id;
  401. if (!this.isAuthorized(chatId)) {
  402. await this.sendMessage(chatId, '❌ 您没有权限使用此机器人');
  403. return;
  404. }
  405. try {
  406. const subscriptions = await Subscription.findAll({
  407. where: { isActive: true },
  408. order: [['createdAt', 'ASC']]
  409. });
  410. if (subscriptions.length === 0) {
  411. await this.sendMessage(chatId, '📭 暂无订阅可删除');
  412. return;
  413. }
  414. let message = `🗑️ *删除订阅*\n\n`;
  415. message += `请选择要删除的订阅:\n\n`;
  416. subscriptions.forEach((sub, index) => {
  417. message += `${index + 1}. *${sub.name}*\n`;
  418. message += ` 📊 节点数:${sub.nodeCount || 0}\n`;
  419. message += ` 🔗 \`${sub.url}\`\n\n`;
  420. });
  421. message += `💡 使用 /delete_1 删除第1个订阅\n`;
  422. message += `💡 使用 /delete_2 删除第2个订阅\n`;
  423. message += `💡 以此类推...`;
  424. await this.sendMessage(chatId, message);
  425. } catch (error) {
  426. logger.error('获取订阅列表失败', { error: error.message });
  427. await this.sendMessage(chatId, '❌ 获取订阅列表失败');
  428. }
  429. }
  430. /**
  431. * 处理 /update_subscriptions 命令
  432. */
  433. async handleUpdateSubscriptions(msg) {
  434. const chatId = msg.chat.id;
  435. if (!this.isAuthorized(chatId)) {
  436. await this.sendMessage(chatId, '❌ 您没有权限使用此机器人');
  437. return;
  438. }
  439. try {
  440. await this.sendMessage(chatId, '🔄 开始手动更新订阅...');
  441. const results = await this.subscriptionManager.manualUpdate();
  442. let message = `✅ *订阅更新完成*\n\n`;
  443. results.forEach(result => {
  444. if (result.error) {
  445. message += `❌ *${result.subscriptionName}*\n`;
  446. message += ` 错误:${result.error}\n\n`;
  447. } else {
  448. message += `✅ *${result.subscriptionName}*\n`;
  449. message += ` 新增:${result.added} 个\n`;
  450. message += ` 更新:${result.updated} 个\n`;
  451. message += ` 移除:${result.removed} 个\n\n`;
  452. }
  453. });
  454. await this.sendMessage(chatId, message);
  455. } catch (error) {
  456. logger.error('手动更新订阅失败', { error: error.message });
  457. await this.sendMessage(chatId, '❌ 更新订阅失败:' + error.message);
  458. }
  459. }
  460. /**
  461. * 处理 /test_speed 命令
  462. */
  463. async handleTestSpeed(msg) {
  464. const chatId = msg.chat.id;
  465. if (!this.isAuthorized(chatId)) {
  466. await this.sendMessage(chatId, '❌ 您没有权限使用此机器人');
  467. return;
  468. }
  469. try {
  470. await this.sendMessage(chatId, `⚡ 开始手动测速...\n当前测速模式: *${this.speedTestMode === 'concurrent' ? '并发连通性' : '串行精准延迟'}*`);
  471. // 获取所有节点(示例,实际请根据你的业务逻辑获取节点列表)
  472. const subscriptions = await Subscription.findAll({ where: { isActive: true } });
  473. let allNodes = [];
  474. for (const sub of subscriptions) {
  475. if (sub.nodes) {
  476. allNodes = allNodes.concat(sub.nodes);
  477. }
  478. }
  479. if (allNodes.length === 0) {
  480. await this.sendMessage(chatId, '❌ 当前没有可用节点');
  481. return;
  482. }
  483. let results;
  484. if (this.speedTestMode === 'concurrent') {
  485. // 并发连通性测试
  486. const SpeedTester = require('./speedTester');
  487. const speedTester = new SpeedTester();
  488. results = await speedTester.testNodes(allNodes, { concurrency: 20 });
  489. } else {
  490. // 串行精准延迟测试
  491. const SpeedTester = require('./speedTester');
  492. const speedTester = new SpeedTester();
  493. results = await speedTester.testNodesSerial(allNodes);
  494. }
  495. // 简要汇总结果
  496. const success = results.filter(r => r.isSuccess);
  497. const failed = results.filter(r => !r.isSuccess);
  498. let msgText = `测速完成!\n成功: ${success.length} 个, 失败: ${failed.length} 个`;
  499. if (success.length > 0) {
  500. msgText += `\n前5个成功节点: ` + success.slice(0, 5).map(r => `${r.nodeName || r.nodeId}(${r.latency}ms)`).join(', ');
  501. }
  502. if (failed.length > 0) {
  503. msgText += `\n前5个失败节点: ` + failed.slice(0, 5).map(r => r.nodeName || r.nodeId).join(', ');
  504. }
  505. await this.sendMessage(chatId, msgText);
  506. } catch (error) {
  507. logger.error('手动测速失败', { error: error.message });
  508. await this.sendMessage(chatId, '❌ 测速失败:' + error.message);
  509. }
  510. }
  511. /**
  512. * 处理测速模式设置命令
  513. */
  514. async handleSetSpeedMode(msg, mode) {
  515. const chatId = msg.chat.id;
  516. if (!this.isAuthorized(chatId)) {
  517. await this.sendMessage(chatId, '❌ 您没有权限使用此机器人');
  518. return;
  519. }
  520. const validModes = ['concurrent', 'serial'];
  521. if (!validModes.includes(mode)) {
  522. await this.sendMessage(chatId, '❌ 无效的测速模式。可选: concurrent(并发连通性), serial(串行精准延迟)');
  523. return;
  524. }
  525. this.speedTestMode = mode;
  526. await this.sendMessage(chatId, `✅ 已切换测速模式为: *${mode === 'concurrent' ? '并发连通性' : '串行精准延迟'}*`);
  527. }
  528. /**
  529. * 检查是否是订阅链接
  530. */
  531. isSubscriptionUrl(text) {
  532. return /^(https?:\/\/|ss:\/\/|vmess:\/\/|trojan:\/\/)/.test(text);
  533. }
  534. /**
  535. * 添加订阅
  536. */
  537. async addSubscription(chatId, url) {
  538. try {
  539. await this.sendMessage(chatId, '📥 正在添加订阅...');
  540. // 创建新订阅
  541. const subscription = await Subscription.create({
  542. name: `订阅_${Date.now()}`,
  543. url: url,
  544. isActive: true,
  545. nodeCount: 0
  546. });
  547. // 更新订阅
  548. const result = await this.subscriptionManager.manualUpdateSubscription(subscription.id);
  549. if (result.error) {
  550. await this.sendMessage(chatId, `❌ 添加订阅失败:${result.error}`);
  551. return;
  552. }
  553. const message = `✅ *订阅添加成功*\n\n` +
  554. `📡 订阅名称:*${subscription.name}*\n` +
  555. `🔗 URL:\`${url}\`\n` +
  556. `📊 节点数:${result.actualNodeCount || 0}\n` +
  557. `➕ 新增:${result.added || 0} 个\n` +
  558. `🔄 更新:${result.updated || 0} 个\n` +
  559. `🗑️ 移除:${result.removed || 0} 个`;
  560. await this.sendMessage(chatId, message);
  561. } catch (error) {
  562. logger.error('添加订阅失败', { error: error.message, url });
  563. await this.sendMessage(chatId, '❌ 添加订阅失败:' + error.message);
  564. }
  565. }
  566. /**
  567. * 处理删除订阅命令
  568. */
  569. async handleDeleteSubscription(msg, index) {
  570. const chatId = msg.chat.id;
  571. if (!this.isAuthorized(chatId)) {
  572. await this.sendMessage(chatId, '❌ 您没有权限使用此机器人');
  573. return;
  574. }
  575. try {
  576. const subscriptions = await Subscription.findAll({
  577. where: { isActive: true },
  578. order: [['createdAt', 'DESC']]
  579. });
  580. if (index < 1 || index > subscriptions.length) {
  581. await this.sendMessage(chatId, '❌ 无效的订阅编号');
  582. return;
  583. }
  584. const subscription = subscriptions[index - 1];
  585. // 标记为非活跃
  586. await subscription.update({ isActive: false });
  587. const message = `🗑️ *订阅删除成功*\n\n` +
  588. `📡 订阅名称:*${subscription.name}*\n` +
  589. `🔗 URL:\`${subscription.url}\`\n` +
  590. `📊 节点数:${subscription.nodeCount || 0}`;
  591. await this.sendMessage(chatId, message);
  592. } catch (error) {
  593. logger.error('删除订阅失败', { error: error.message, index });
  594. await this.sendMessage(chatId, '❌ 删除订阅失败:' + error.message);
  595. }
  596. }
  597. /**
  598. * 根据索引删除订阅(保留兼容性)
  599. */
  600. async removeSubscriptionByIndex(chatId, index) {
  601. try {
  602. const subscriptions = await Subscription.findAll({
  603. where: { isActive: true },
  604. order: [['createdAt', 'DESC']]
  605. });
  606. if (index < 1 || index > subscriptions.length) {
  607. await this.sendMessage(chatId, '❌ 无效的订阅编号');
  608. return;
  609. }
  610. const subscription = subscriptions[index - 1];
  611. // 标记为非活跃
  612. await subscription.update({ isActive: false });
  613. const message = `🗑️ *订阅删除成功*\n\n` +
  614. `📡 订阅名称:*${subscription.name}*\n` +
  615. `🔗 URL:\`${subscription.url}\`\n` +
  616. `📊 节点数:${subscription.nodeCount || 0}`;
  617. await this.sendMessage(chatId, message);
  618. } catch (error) {
  619. logger.error('删除订阅失败', { error: error.message, index });
  620. await this.sendMessage(chatId, '❌ 删除订阅失败:' + error.message);
  621. }
  622. }
  623. /**
  624. * 获取运行时间
  625. */
  626. getUptime() {
  627. const uptime = process.uptime();
  628. const hours = Math.floor(uptime / 3600);
  629. const minutes = Math.floor((uptime % 3600) / 60);
  630. return `${hours}小时${minutes}分钟`;
  631. }
  632. /**
  633. * 获取内存使用情况
  634. */
  635. getMemoryUsage() {
  636. const usage = process.memoryUsage();
  637. const used = Math.round(usage.heapUsed / 1024 / 1024);
  638. const total = Math.round(usage.heapTotal / 1024 / 1024);
  639. return `${used}MB / ${total}MB`;
  640. }
  641. /**
  642. * 停止机器人
  643. */
  644. stop() {
  645. if (this.enabled && this.bot) {
  646. this.bot.stopPolling();
  647. logger.info('Telegram机器人已停止');
  648. }
  649. }
  650. }
  651. module.exports = BotManager;