feat: Implement a proxy pool with concurrent testing and integrate proxy management commands into the Telegram bot.

This commit is contained in:
2026-02-11 02:10:11 +08:00
parent 2ff52d5d73
commit be8dd745fb
5 changed files with 537 additions and 5 deletions

View File

@@ -197,6 +197,10 @@ class ProvisionerBot:
("schedule", self.cmd_schedule),
("schedule_config", self.cmd_schedule_config),
("schedule_status", self.cmd_schedule_status),
# 代理池管理
("proxy_status", self.cmd_proxy_status),
("proxy_test", self.cmd_proxy_test),
("proxy_reload", self.cmd_proxy_reload),
]
for cmd, handler in handlers:
self.app.add_handler(CommandHandler(cmd, handler))
@@ -362,6 +366,10 @@ class ProvisionerBot:
BotCommand("schedule", "定时调度器 开关"),
BotCommand("schedule_config", "调度器参数配置"),
BotCommand("schedule_status", "调度器运行状态"),
# 代理池
BotCommand("proxy_status", "代理池状态"),
BotCommand("proxy_test", "测试并清理代理"),
BotCommand("proxy_reload", "重新加载代理"),
]
try:
await self.app.bot.set_my_commands(commands)
@@ -451,6 +459,11 @@ class ProvisionerBot:
/schedule_config - 配置调度器参数
/schedule_status - 查看调度器运行状态
<b>🌐 代理池管理:</b>
/proxy_status - 查看代理池状态
/proxy_test - 测试并清理不可用代理
/proxy_reload - 从 proxy.txt 重新加载代理
<b>💡 示例:</b>
<code>/list</code> - 查看所有待处理账号
<code>/run 0</code> - 处理第一个 Team
@@ -3949,6 +3962,45 @@ class ProvisionerBot:
import json
import threading
# ===== 代理池预检测 =====
try:
import proxy_pool
pool_count = proxy_pool.get_proxy_count()
if pool_count == 0:
# 尝试加载
pool_count = proxy_pool.reload_proxies()
if pool_count > 0:
await self.app.bot.send_message(
chat_id,
f"🌐 <b>代理池预检测</b>\n\n"
f"正在测试 {pool_count} 个代理 (20 并发)...",
parse_mode="HTML"
)
loop = asyncio.get_event_loop()
test_result = await loop.run_in_executor(
self.executor,
lambda: proxy_pool.test_and_clean_proxies(concurrency=20)
)
await self.app.bot.send_message(
chat_id,
f"✅ <b>代理池就绪</b>\n\n"
f"总计: {test_result['total']} | "
f"存活: {test_result['alive']} | "
f"移除: {test_result['removed']}\n"
f"耗时: {test_result['duration']}s",
parse_mode="HTML"
)
if test_result['alive'] == 0:
await self.app.bot.send_message(
chat_id,
"⚠️ 所有代理都不可用,将使用直连或静态代理",
)
except ImportError:
pass
except Exception as e:
log.warning(f"代理池预检测异常: {e}")
results = []
success_count = 0
fail_count = 0
@@ -6332,6 +6384,126 @@ class ProvisionerBot:
except Exception as e:
await update.message.reply_text(f"❌ 添加 IBAN 失败: {e}")
# ==================== 代理池管理 ====================
@admin_only
async def cmd_proxy_status(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""查看代理池状态"""
try:
import proxy_pool
status = proxy_pool.get_proxy_status()
count = status["count"]
last_test = status["last_test_results"]
lines = [f"<b>🌐 代理池状态</b>\n"]
lines.append(f"可用代理: {count}")
if last_test:
lines.append(f"\n<b>上次测试:</b>")
lines.append(f" 总计: {last_test.get('total', 0)}")
lines.append(f" 存活: {last_test.get('alive', 0)}")
lines.append(f" 移除: {last_test.get('removed', 0)}")
lines.append(f" 耗时: {last_test.get('duration', 0)}s")
if status["last_test_time"]:
from datetime import datetime
test_time = datetime.fromtimestamp(status["last_test_time"])
lines.append(f" 时间: {test_time.strftime('%H:%M:%S')}")
if count > 0:
# 显示前5个代理 (脱敏)
lines.append(f"\n<b>代理列表</b> (前 5 个):")
for i, proxy in enumerate(status["proxies"][:5]):
# 脱敏: 隐藏密码部分
display = proxy
if "@" in proxy:
parts = proxy.split("@")
scheme_auth = parts[0]
host_part = parts[1]
# 取 scheme 部分
if "://" in scheme_auth:
scheme = scheme_auth.split("://")[0]
display = f"{scheme}://***@{host_part}"
else:
display = f"***@{host_part}"
lines.append(f" {i+1}. <code>{display}</code>")
if count > 5:
lines.append(f" ... 还有 {count - 5}")
else:
lines.append(f"\n💡 将代理添加到 <code>proxy.txt</code> 然后使用 /proxy_reload 加载")
await update.message.reply_text("\n".join(lines), parse_mode="HTML")
except ImportError:
await update.message.reply_text("❌ proxy_pool 模块未找到")
except Exception as e:
await update.message.reply_text(f"❌ 获取代理池状态失败: {e}")
@admin_only
async def cmd_proxy_test(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""测试并清理代理"""
try:
import proxy_pool
# 先加载最新
pool_count = proxy_pool.reload_proxies()
if pool_count == 0:
await update.message.reply_text(
"📭 <b>代理池为空</b>\n\n"
"请将代理添加到 <code>proxy.txt</code> 后重试",
parse_mode="HTML"
)
return
msg = await update.message.reply_text(
f"🔄 <b>正在测试代理</b>\n\n"
f"代理数量: {pool_count}\n"
f"并发: 20\n"
f"请稍候...",
parse_mode="HTML"
)
loop = asyncio.get_event_loop()
result = await loop.run_in_executor(
self.executor,
lambda: proxy_pool.test_and_clean_proxies(concurrency=20)
)
await msg.edit_text(
f"✅ <b>代理测试完成</b>\n\n"
f"总计: {result['total']}\n"
f"存活: {result['alive']}\n"
f"移除: {result['removed']}\n"
f"耗时: {result['duration']}s\n\n"
f"{'💡 不可用代理已从 proxy.txt 中移除' if result['removed'] > 0 else '🎉 所有代理均可用'}",
parse_mode="HTML"
)
except ImportError:
await update.message.reply_text("❌ proxy_pool 模块未找到")
except Exception as e:
await update.message.reply_text(f"❌ 代理测试失败: {e}")
@admin_only
async def cmd_proxy_reload(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""从 proxy.txt 重新加载代理"""
try:
import proxy_pool
count = proxy_pool.reload_proxies()
await update.message.reply_text(
f"✅ <b>代理池已重新加载</b>\n\n"
f"已加载: {count} 个代理\n\n"
f"{'💡 使用 /proxy_test 测试代理可用性' if count > 0 else '📭 proxy.txt 为空或不存在'}",
parse_mode="HTML"
)
except ImportError:
await update.message.reply_text("❌ proxy_pool 模块未找到")
except Exception as e:
await update.message.reply_text(f"❌ 重新加载失败: {e}")
# ==================== 定时调度器 ====================
@admin_only
@@ -6868,6 +7040,39 @@ class ProvisionerBot:
import json
import threading
# ===== 代理池预检测 =====
try:
import proxy_pool
pool_count = proxy_pool.get_proxy_count()
if pool_count == 0:
pool_count = proxy_pool.reload_proxies()
if pool_count > 0:
log.info(f"[Scheduler] 代理池预检测: 测试 {pool_count} 个代理...")
loop = asyncio.get_event_loop()
test_result = await loop.run_in_executor(
self.executor,
lambda: proxy_pool.test_and_clean_proxies(concurrency=20)
)
log.info(f"[Scheduler] 代理池: 存活 {test_result['alive']}/{test_result['total']},移除 {test_result['removed']}")
if chat_id:
try:
await self.app.bot.send_message(
chat_id,
f"🌐 <b>代理池预检测</b>\n\n"
f"存活: {test_result['alive']}/{test_result['total']} | "
f"移除: {test_result['removed']} | "
f"耗时: {test_result['duration']}s",
parse_mode="HTML"
)
except Exception:
pass
except ImportError:
pass
except Exception as e:
log.warning(f"代理池预检测异常: {e}")
results = []
success_count = 0
fail_count = 0