主要更新: - ✨ 新增 Telegram Bot 交互界面 - ✨ 新增欧洲账单自动生成功能 - 📦 整理项目结构,部署文件移至 deployment/ 目录 - 📝 完善文档,新增 CHANGELOG 和 Bot 部署指南 - 🔧 统一使用 pyproject.toml 管理依赖(支持 uv) - 🛡️ 增强 .gitignore,防止敏感配置泄露 新增文件: - tg_bot.py: Telegram Bot 主程序 - generate_billing.py: 独立账单生成工具 - modules/billing.py: 欧洲账单生成模块 - deployment/: Docker、systemd 等部署配置 - docs/: 完整的文档和更新日志 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
277 lines
9.0 KiB
Python
Executable File
277 lines
9.0 KiB
Python
Executable File
#!/usr/bin/env python3
|
||
"""自动化注册 OpenAI 账号(使用临时邮箱)
|
||
|
||
使用方式:
|
||
python auto_register.py # 注册 1 个账号
|
||
python auto_register.py --count 10 # 注册 10 个账号
|
||
python auto_register.py --password mypass # 指定密码
|
||
"""
|
||
|
||
import sys
|
||
import argparse
|
||
import secrets
|
||
import json
|
||
from typing import List, Dict
|
||
|
||
from modules.register import OpenAIRegistrar
|
||
from modules.tempmail import TempMailClient
|
||
from config import TEMPMAIL_CONFIG, DEBUG
|
||
|
||
|
||
def generate_random_password(length: int = 16) -> str:
|
||
"""生成随机密码"""
|
||
# 包含大小写字母、数字、特殊字符
|
||
chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*'
|
||
return ''.join(secrets.choice(chars) for _ in range(length))
|
||
|
||
|
||
def register_single_account(tempmail_client: TempMailClient, password: str = None, generate_billing: bool = False) -> Dict:
|
||
"""注册单个账号
|
||
|
||
Args:
|
||
tempmail_client: 临时邮箱客户端
|
||
password: 密码(None 则自动生成)
|
||
generate_billing: 是否生成欧洲账单 URL
|
||
|
||
Returns:
|
||
注册结果(包含可选的 checkout_url)
|
||
"""
|
||
# 生成密码(如果未指定)
|
||
if not password:
|
||
password = generate_random_password()
|
||
|
||
# 创建注册器
|
||
registrar = OpenAIRegistrar(tempmail_client=tempmail_client)
|
||
|
||
# 执行注册(自动生成邮箱 + 验证)
|
||
result = registrar.register_with_auto_email(password)
|
||
|
||
# 如果注册成功且需要生成账单
|
||
if result.get('success') and generate_billing:
|
||
try:
|
||
# Step 5: 获取 access token
|
||
access_token = registrar._step5_get_access_token()
|
||
|
||
# 生成欧洲账单 URL
|
||
from modules.billing import EUBillingGenerator
|
||
billing_gen = EUBillingGenerator()
|
||
billing_result = billing_gen.generate_checkout_url(access_token)
|
||
|
||
if billing_result.success:
|
||
result['checkout_url'] = billing_result.checkout_url
|
||
if DEBUG:
|
||
print(f"✅ EU billing URL generated")
|
||
else:
|
||
result['billing_error'] = billing_result.error
|
||
if DEBUG:
|
||
print(f"⚠️ Billing generation failed: {billing_result.error}")
|
||
|
||
except Exception as e:
|
||
result['billing_error'] = str(e)
|
||
if DEBUG:
|
||
print(f"❌ Billing generation exception: {e}")
|
||
|
||
return result
|
||
|
||
|
||
def register_multiple_accounts(count: int, password: str = None, save_to_file: str = None, generate_billing: bool = False):
|
||
"""批量注册账号
|
||
|
||
Args:
|
||
count: 注册数量
|
||
password: 密码(None 则每个账号生成不同密码)
|
||
save_to_file: 保存成功账号的文件路径(JSON 格式)
|
||
generate_billing: 是否生成欧洲账单 URL
|
||
"""
|
||
print(f"\n{'='*60}")
|
||
print(f"Starting batch registration: {count} accounts")
|
||
if generate_billing:
|
||
print(f"EU Billing: Enabled")
|
||
print(f"{'='*60}\n")
|
||
|
||
# 检查临时邮箱配置
|
||
api_base_url = TEMPMAIL_CONFIG.get('api_base_url')
|
||
username = TEMPMAIL_CONFIG.get('username')
|
||
password_cfg = TEMPMAIL_CONFIG.get('password')
|
||
admin_token = TEMPMAIL_CONFIG.get('admin_token')
|
||
|
||
if not api_base_url or 'your.tempmail.domain' in api_base_url:
|
||
print("❌ Error: TEMPMAIL_CONFIG.api_base_url not configured in config.py")
|
||
sys.exit(1)
|
||
|
||
# 检查认证方式:优先用户名密码,其次 Token
|
||
if username and password_cfg:
|
||
if 'your_password_here' in password_cfg:
|
||
print("❌ Error: TEMPMAIL_CONFIG.password not configured in config.py")
|
||
print(" Please set username and password")
|
||
sys.exit(1)
|
||
|
||
# 初始化临时邮箱客户端(用户名密码方式)
|
||
tempmail_client = TempMailClient(
|
||
api_base_url=api_base_url,
|
||
username=username,
|
||
password=password_cfg
|
||
)
|
||
|
||
elif admin_token:
|
||
if 'your_jwt_token_here' in admin_token:
|
||
print("❌ Error: TEMPMAIL_CONFIG.admin_token not configured in config.py")
|
||
sys.exit(1)
|
||
|
||
# 初始化临时邮箱客户端(Token 方式)
|
||
tempmail_client = TempMailClient(
|
||
api_base_url=api_base_url,
|
||
admin_token=admin_token
|
||
)
|
||
|
||
else:
|
||
print("❌ Error: TEMPMAIL_CONFIG not properly configured in config.py")
|
||
print(" Please provide either (username, password) or admin_token")
|
||
sys.exit(1)
|
||
|
||
# 统计
|
||
success_accounts = []
|
||
failed_accounts = []
|
||
|
||
# 批量注册
|
||
for i in range(1, count + 1):
|
||
print(f"\n{'─'*60}")
|
||
print(f"[{i}/{count}] Registering account #{i}...")
|
||
print(f"{'─'*60}")
|
||
|
||
try:
|
||
# 注册单个账号
|
||
result = register_single_account(
|
||
tempmail_client=tempmail_client,
|
||
password=password, # None 则自动生成
|
||
generate_billing=generate_billing
|
||
)
|
||
|
||
if result.get('success'):
|
||
account_info = {
|
||
'email': result['email'],
|
||
'password': result['password'],
|
||
'verified': result.get('verified', False),
|
||
}
|
||
|
||
# 如果生成了账单 URL,添加到输出
|
||
if 'checkout_url' in result:
|
||
account_info['checkout_url'] = result['checkout_url']
|
||
|
||
success_accounts.append(account_info)
|
||
|
||
print(f"\n✅ Account #{i} registered successfully!")
|
||
print(f" Email: {account_info['email']}")
|
||
print(f" Password: {account_info['password']}")
|
||
|
||
# 如果有账单 URL,打印出来
|
||
if 'checkout_url' in account_info:
|
||
print(f" Checkout URL: {account_info['checkout_url']}")
|
||
|
||
else:
|
||
failed_info = {
|
||
'email': result.get('email', 'N/A'),
|
||
'error': result.get('error', 'Unknown error'),
|
||
}
|
||
failed_accounts.append(failed_info)
|
||
|
||
print(f"\n❌ Account #{i} failed")
|
||
print(f" Email: {failed_info['email']}")
|
||
print(f" Error: {failed_info['error']}")
|
||
|
||
except KeyboardInterrupt:
|
||
print(f"\n\n❌ Interrupted by user at account #{i}")
|
||
break
|
||
|
||
except Exception as e:
|
||
print(f"\n❌ Unexpected error at account #{i}: {e}")
|
||
failed_accounts.append({
|
||
'email': 'N/A',
|
||
'error': str(e),
|
||
})
|
||
|
||
# 打印最终统计
|
||
print(f"\n{'='*60}")
|
||
print(f"BATCH REGISTRATION COMPLETE")
|
||
print(f"{'='*60}")
|
||
print(f"Total: {count} accounts")
|
||
print(f"Success: {len(success_accounts)} accounts ✅")
|
||
print(f"Failed: {len(failed_accounts)} accounts ❌")
|
||
print(f"{'='*60}\n")
|
||
|
||
# 保存成功的账号
|
||
if success_accounts:
|
||
if save_to_file:
|
||
output_file = save_to_file
|
||
else:
|
||
output_file = "registered_accounts.json"
|
||
|
||
with open(output_file, 'w', encoding='utf-8') as f:
|
||
json.dump(success_accounts, f, indent=2, ensure_ascii=False)
|
||
|
||
print(f"✅ Saved {len(success_accounts)} accounts to: {output_file}")
|
||
|
||
# 打印成功账号列表
|
||
if success_accounts:
|
||
print(f"\n{'─'*60}")
|
||
print(f"SUCCESSFUL ACCOUNTS:")
|
||
print(f"{'─'*60}")
|
||
for idx, acc in enumerate(success_accounts, 1):
|
||
print(f"{idx}. {acc['email']} | {acc['password']}")
|
||
print(f"{'─'*60}\n")
|
||
|
||
|
||
def main():
|
||
parser = argparse.ArgumentParser(
|
||
description='自动化注册 OpenAI 账号(使用临时邮箱)',
|
||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||
epilog="""
|
||
示例:
|
||
python auto_register.py # 注册 1 个账号
|
||
python auto_register.py --count 10 # 注册 10 个账号
|
||
python auto_register.py --password mypass # 指定密码(所有账号相同)
|
||
python auto_register.py -c 5 -o out.json # 注册 5 个,保存到 out.json
|
||
"""
|
||
)
|
||
|
||
parser.add_argument(
|
||
'-c', '--count',
|
||
type=int,
|
||
default=1,
|
||
help='注册账号数量(默认: 1)'
|
||
)
|
||
|
||
parser.add_argument(
|
||
'-p', '--password',
|
||
type=str,
|
||
default=None,
|
||
help='指定密码(默认: 自动生成随机密码)'
|
||
)
|
||
|
||
parser.add_argument(
|
||
'-o', '--output',
|
||
type=str,
|
||
default=None,
|
||
help='保存成功账号的 JSON 文件路径(默认: registered_accounts.json)'
|
||
)
|
||
|
||
parser.add_argument(
|
||
'--eu-billing',
|
||
action='store_true',
|
||
help='注册后自动生成欧洲账单 checkout URL'
|
||
)
|
||
|
||
args = parser.parse_args()
|
||
|
||
# 执行批量注册
|
||
register_multiple_accounts(
|
||
count=args.count,
|
||
password=args.password,
|
||
save_to_file=args.output,
|
||
generate_billing=args.eu_billing
|
||
)
|
||
|
||
|
||
if __name__ == '__main__':
|
||
main()
|