feat(telegram_bot): Add verify_all command for force account re-verification
- Add new /verify_all command to force re-verify all accounts with tokens - Update command registration to include verify_all handler - Modify _validate_and_cleanup_accounts() to accept force_all parameter - Add progress bar visualization using block characters (█░) - Implement progress bar helper function with percentage display - Update verification logic to handle two modes: normal and force-all - Enhance user messages with token count and mode-specific text - Update help text and command descriptions in Chinese - Improve progress tracking with visual feedback during verification - Separate /verify (unverified only) from /verify_all (all with tokens)
This commit is contained in:
145
telegram_bot.py
145
telegram_bot.py
@@ -137,6 +137,7 @@ class ProvisionerBot:
|
|||||||
("dashboard", self.cmd_dashboard),
|
("dashboard", self.cmd_dashboard),
|
||||||
("import", self.cmd_import),
|
("import", self.cmd_import),
|
||||||
("verify", self.cmd_verify),
|
("verify", self.cmd_verify),
|
||||||
|
("verify_all", self.cmd_verify_all),
|
||||||
("stock", self.cmd_stock),
|
("stock", self.cmd_stock),
|
||||||
("gptmail_keys", self.cmd_gptmail_keys),
|
("gptmail_keys", self.cmd_gptmail_keys),
|
||||||
("gptmail_add", self.cmd_gptmail_add),
|
("gptmail_add", self.cmd_gptmail_add),
|
||||||
@@ -270,7 +271,8 @@ class ProvisionerBot:
|
|||||||
BotCommand("stock", "查看账号库存"),
|
BotCommand("stock", "查看账号库存"),
|
||||||
BotCommand("s2a_config", "配置 S2A 参数"),
|
BotCommand("s2a_config", "配置 S2A 参数"),
|
||||||
BotCommand("import", "导入账号到 team.json"),
|
BotCommand("import", "导入账号到 team.json"),
|
||||||
BotCommand("verify", "验证账号并移除无效账号"),
|
BotCommand("verify", "验证未验证的账号"),
|
||||||
|
BotCommand("verify_all", "强制重新验证所有账号"),
|
||||||
# GPTMail
|
# GPTMail
|
||||||
BotCommand("gptmail_keys", "查看 GPTMail API Keys"),
|
BotCommand("gptmail_keys", "查看 GPTMail API Keys"),
|
||||||
BotCommand("gptmail_add", "添加 GPTMail API Key"),
|
BotCommand("gptmail_add", "添加 GPTMail API Key"),
|
||||||
@@ -338,7 +340,8 @@ class ProvisionerBot:
|
|||||||
|
|
||||||
<b>📤 导入账号:</b>
|
<b>📤 导入账号:</b>
|
||||||
/import - 导入账号到 team.json
|
/import - 导入账号到 team.json
|
||||||
/verify - 验证账号并移除无效账号
|
/verify - 验证未验证的账号
|
||||||
|
/verify_all - 强制重新验证所有账号
|
||||||
或直接发送 JSON 文件
|
或直接发送 JSON 文件
|
||||||
|
|
||||||
<b>📧 GPTMail 管理:</b>
|
<b>📧 GPTMail 管理:</b>
|
||||||
@@ -2041,20 +2044,23 @@ class ProvisionerBot:
|
|||||||
if added_count > 0:
|
if added_count > 0:
|
||||||
await self._validate_and_cleanup_accounts(chat_id)
|
await self._validate_and_cleanup_accounts(chat_id)
|
||||||
|
|
||||||
async def _validate_and_cleanup_accounts(self, chat_id: int):
|
async def _validate_and_cleanup_accounts(self, chat_id: int, force_all: bool = False):
|
||||||
"""验证新导入账号的 account_id,移除无效账号
|
"""验证新导入账号的 account_id,移除无效账号
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
chat_id: Telegram 聊天 ID,用于发送进度消息
|
chat_id: Telegram 聊天 ID,用于发送进度消息
|
||||||
|
force_all: 是否强制重新验证所有有 token 的账号
|
||||||
"""
|
"""
|
||||||
import json
|
import json
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||||
|
|
||||||
|
mode_text = "重新验证" if force_all else "验证"
|
||||||
|
|
||||||
# 发送开始验证的消息
|
# 发送开始验证的消息
|
||||||
progress_msg = await self.app.bot.send_message(
|
progress_msg = await self.app.bot.send_message(
|
||||||
chat_id=chat_id,
|
chat_id=chat_id,
|
||||||
text="<b>🔍 正在验证账号...</b>\n\n⏳ 获取 account_id 中...",
|
text=f"<b>🔍 正在{mode_text}账号...</b>\n\n⏳ 获取 account_id 中...",
|
||||||
parse_mode="HTML"
|
parse_mode="HTML"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -2075,13 +2081,19 @@ class ProvisionerBot:
|
|||||||
if not isinstance(accounts, list):
|
if not isinstance(accounts, list):
|
||||||
accounts = [accounts]
|
accounts = [accounts]
|
||||||
|
|
||||||
# 筛选需要验证的账号 (有 token 但没有 account_id)
|
# 筛选需要验证的账号
|
||||||
accounts_to_verify = []
|
accounts_to_verify = []
|
||||||
for i, acc in enumerate(accounts):
|
for i, acc in enumerate(accounts):
|
||||||
token = acc.get("token", "")
|
token = acc.get("token", "")
|
||||||
account_id = acc.get("account_id", "")
|
account_id = acc.get("account_id", "")
|
||||||
if token and not account_id:
|
if force_all:
|
||||||
accounts_to_verify.append((i, acc))
|
# 强制模式:验证所有有 token 的账号
|
||||||
|
if token:
|
||||||
|
accounts_to_verify.append((i, acc))
|
||||||
|
else:
|
||||||
|
# 普通模式:只验证有 token 但没有 account_id 的账号
|
||||||
|
if token and not account_id:
|
||||||
|
accounts_to_verify.append((i, acc))
|
||||||
|
|
||||||
if not accounts_to_verify:
|
if not accounts_to_verify:
|
||||||
await self.app.bot.edit_message_text(
|
await self.app.bot.edit_message_text(
|
||||||
@@ -2093,10 +2105,23 @@ class ProvisionerBot:
|
|||||||
return
|
return
|
||||||
|
|
||||||
total = len(accounts_to_verify)
|
total = len(accounts_to_verify)
|
||||||
|
|
||||||
|
# 生成进度条的辅助函数
|
||||||
|
def make_progress_bar(current: int, total: int, width: int = 16) -> str:
|
||||||
|
"""生成进度条字符串"""
|
||||||
|
if total == 0:
|
||||||
|
return "[" + "░" * width + "]"
|
||||||
|
filled = int(width * current / total)
|
||||||
|
empty = width - filled
|
||||||
|
percent = int(100 * current / total)
|
||||||
|
return f"[{'█' * filled}{'░' * empty}] {current}/{total} ({percent}%)"
|
||||||
|
|
||||||
await self.app.bot.edit_message_text(
|
await self.app.bot.edit_message_text(
|
||||||
chat_id=chat_id,
|
chat_id=chat_id,
|
||||||
message_id=progress_msg.message_id,
|
message_id=progress_msg.message_id,
|
||||||
text=f"<b>🔍 正在验证账号...</b>\n\n⏳ 验证 {total} 个账号的 account_id (20 并发)...",
|
text=f"<b>🔍 正在{mode_text}账号...</b>\n\n"
|
||||||
|
f"<code>{make_progress_bar(0, total)}</code>\n\n"
|
||||||
|
f"⏳ 验证 {total} 个账号的 account_id (20 并发)...",
|
||||||
parse_mode="HTML"
|
parse_mode="HTML"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -2148,11 +2173,12 @@ class ProvisionerBot:
|
|||||||
if completed_count % 10 == 0 or completed_count == total or current_time - last_update_time > 1:
|
if completed_count % 10 == 0 or completed_count == total or current_time - last_update_time > 1:
|
||||||
last_update_time = current_time
|
last_update_time = current_time
|
||||||
try:
|
try:
|
||||||
|
progress_bar = make_progress_bar(completed_count, total)
|
||||||
await self.app.bot.edit_message_text(
|
await self.app.bot.edit_message_text(
|
||||||
chat_id=chat_id,
|
chat_id=chat_id,
|
||||||
message_id=progress_msg.message_id,
|
message_id=progress_msg.message_id,
|
||||||
text=f"<b>🔍 正在验证账号...</b>\n\n"
|
text=f"<b>🔍 正在{mode_text}账号...</b>\n\n"
|
||||||
f"进度: {completed_count}/{total}\n"
|
f"<code>{progress_bar}</code>\n\n"
|
||||||
f"✅ 成功: {len(valid_indices)}\n"
|
f"✅ 成功: {len(valid_indices)}\n"
|
||||||
f"❌ 失败: {len(failed_accounts)}",
|
f"❌ 失败: {len(failed_accounts)}",
|
||||||
parse_mode="HTML"
|
parse_mode="HTML"
|
||||||
@@ -2176,6 +2202,8 @@ class ProvisionerBot:
|
|||||||
lines = [
|
lines = [
|
||||||
"<b>✅ 账号验证完成</b>",
|
"<b>✅ 账号验证完成</b>",
|
||||||
"",
|
"",
|
||||||
|
f"<code>{make_progress_bar(total, total)}</code>",
|
||||||
|
"",
|
||||||
f"📊 验证结果:",
|
f"📊 验证结果:",
|
||||||
f" • 验证总数: {total}",
|
f" • 验证总数: {total}",
|
||||||
f" • 成功: {len(valid_indices)}",
|
f" • 成功: {len(valid_indices)}",
|
||||||
@@ -2217,7 +2245,83 @@ class ProvisionerBot:
|
|||||||
|
|
||||||
@admin_only
|
@admin_only
|
||||||
async def cmd_verify(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
|
async def cmd_verify(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||||
"""手动验证 team.json 中的账号,获取 account_id 并移除无效账号"""
|
"""手动验证 team.json 中的账号,获取 account_id 并移除无效账号
|
||||||
|
|
||||||
|
用法:
|
||||||
|
/verify - 验证未验证的账号
|
||||||
|
/verify_all - 强制重新验证所有账号
|
||||||
|
"""
|
||||||
|
chat_id = update.effective_chat.id
|
||||||
|
|
||||||
|
# 检查是否有任务正在运行
|
||||||
|
if self.current_task and not self.current_task.done():
|
||||||
|
await update.message.reply_text(
|
||||||
|
f"⚠️ 有任务正在运行: {self.current_team}\n"
|
||||||
|
"请等待任务完成或使用 /stop 停止后再验证"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
# 解析参数
|
||||||
|
force_all = context.args and context.args[0].lower() == "all"
|
||||||
|
|
||||||
|
# 检查 team.json 是否存在
|
||||||
|
from pathlib import Path
|
||||||
|
team_json_path = Path(TEAM_JSON_FILE)
|
||||||
|
if not team_json_path.exists():
|
||||||
|
await update.message.reply_text("❌ team.json 不存在,请先导入账号")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 统计需要验证的账号
|
||||||
|
import json
|
||||||
|
try:
|
||||||
|
with open(team_json_path, "r", encoding="utf-8") as f:
|
||||||
|
accounts = json.load(f)
|
||||||
|
if not isinstance(accounts, list):
|
||||||
|
accounts = [accounts]
|
||||||
|
except Exception as e:
|
||||||
|
await update.message.reply_text(f"❌ 读取 team.json 失败: {e}")
|
||||||
|
return
|
||||||
|
|
||||||
|
total_accounts = len(accounts)
|
||||||
|
has_token = sum(1 for acc in accounts if acc.get("token"))
|
||||||
|
already_verified = sum(1 for acc in accounts if acc.get("account_id"))
|
||||||
|
|
||||||
|
if force_all:
|
||||||
|
# 强制重新验证所有有 token 的账号
|
||||||
|
need_verify = has_token
|
||||||
|
else:
|
||||||
|
# 只验证未验证的账号
|
||||||
|
need_verify = sum(1 for acc in accounts if acc.get("token") and not acc.get("account_id"))
|
||||||
|
|
||||||
|
if need_verify == 0:
|
||||||
|
await update.message.reply_text(
|
||||||
|
f"<b>✅ 无需验证</b>\n\n"
|
||||||
|
f"team.json 共 {total_accounts} 个账号\n"
|
||||||
|
f"有 Token: {has_token}\n"
|
||||||
|
f"已验证: {already_verified}\n"
|
||||||
|
f"待验证: 0\n\n"
|
||||||
|
f"💡 使用 /verify_all 强制重新验证所有账号",
|
||||||
|
parse_mode="HTML"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
mode_text = "强制重新验证" if force_all else "验证"
|
||||||
|
await update.message.reply_text(
|
||||||
|
f"<b>🔍 开始{mode_text}账号</b>\n\n"
|
||||||
|
f"team.json 共 {total_accounts} 个账号\n"
|
||||||
|
f"有 Token: {has_token}\n"
|
||||||
|
f"已验证: {already_verified}\n"
|
||||||
|
f"待验证: {need_verify}\n\n"
|
||||||
|
f"⏳ 正在验证...",
|
||||||
|
parse_mode="HTML"
|
||||||
|
)
|
||||||
|
|
||||||
|
# 执行验证
|
||||||
|
await self._validate_and_cleanup_accounts(chat_id, force_all=force_all)
|
||||||
|
|
||||||
|
@admin_only
|
||||||
|
async def cmd_verify_all(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||||
|
"""强制重新验证所有账号"""
|
||||||
chat_id = update.effective_chat.id
|
chat_id = update.effective_chat.id
|
||||||
|
|
||||||
# 检查是否有任务正在运行
|
# 检查是否有任务正在运行
|
||||||
@@ -2247,30 +2351,31 @@ class ProvisionerBot:
|
|||||||
return
|
return
|
||||||
|
|
||||||
total_accounts = len(accounts)
|
total_accounts = len(accounts)
|
||||||
need_verify = sum(1 for acc in accounts if acc.get("token") and not acc.get("account_id"))
|
has_token = sum(1 for acc in accounts if acc.get("token"))
|
||||||
already_verified = sum(1 for acc in accounts if acc.get("account_id"))
|
already_verified = sum(1 for acc in accounts if acc.get("account_id"))
|
||||||
|
|
||||||
if need_verify == 0:
|
if has_token == 0:
|
||||||
await update.message.reply_text(
|
await update.message.reply_text(
|
||||||
f"<b>✅ 无需验证</b>\n\n"
|
f"<b>❌ 无法验证</b>\n\n"
|
||||||
f"team.json 共 {total_accounts} 个账号\n"
|
f"team.json 共 {total_accounts} 个账号\n"
|
||||||
f"已验证: {already_verified}\n"
|
f"有 Token: 0\n\n"
|
||||||
f"待验证: 0",
|
f"没有可验证的账号",
|
||||||
parse_mode="HTML"
|
parse_mode="HTML"
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
await update.message.reply_text(
|
await update.message.reply_text(
|
||||||
f"<b>🔍 开始验证账号</b>\n\n"
|
f"<b>🔍 开始强制重新验证所有账号</b>\n\n"
|
||||||
f"team.json 共 {total_accounts} 个账号\n"
|
f"team.json 共 {total_accounts} 个账号\n"
|
||||||
|
f"有 Token: {has_token}\n"
|
||||||
f"已验证: {already_verified}\n"
|
f"已验证: {already_verified}\n"
|
||||||
f"待验证: {need_verify}\n\n"
|
f"待验证: {has_token}\n\n"
|
||||||
f"⏳ 正在验证...",
|
f"⏳ 正在验证...",
|
||||||
parse_mode="HTML"
|
parse_mode="HTML"
|
||||||
)
|
)
|
||||||
|
|
||||||
# 执行验证
|
# 执行验证 (强制模式)
|
||||||
await self._validate_and_cleanup_accounts(chat_id)
|
await self._validate_and_cleanup_accounts(chat_id, force_all=True)
|
||||||
|
|
||||||
async def _import_batch_timeout_callback(self, context: ContextTypes.DEFAULT_TYPE):
|
async def _import_batch_timeout_callback(self, context: ContextTypes.DEFAULT_TYPE):
|
||||||
"""批量导入超时回调 - 由 job_queue 调用"""
|
"""批量导入超时回调 - 由 job_queue 调用"""
|
||||||
|
|||||||
Reference in New Issue
Block a user