Files
AutoDoneTeam/generate_billing.py
dela d146ad9ebd feat: 添加完整的 Telegram Bot 和欧洲账单生成功能
主要更新:
-  新增 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>
2026-01-11 09:59:13 +08:00

271 lines
8.0 KiB
Python
Executable File

#!/usr/bin/env python3
"""Batch EU Billing Generator for Registered Accounts
Reads accounts from registered_accounts.json and generates EU billing checkout URLs.
Usage:
python generate_billing.py # Process registered_accounts.json
python generate_billing.py --input custom.json # Process custom file
python generate_billing.py --email user@example.com --password pass # Single account
"""
import argparse
import json
import sys
from typing import List, Dict, Optional
from datetime import datetime
from modules.billing import EUBillingGenerator
from modules.register import OpenAIRegistrar
from modules.tempmail import TempMailClient
from config import TEMPMAIL_CONFIG, DEBUG
def login_and_get_token(email: str, password: str) -> Optional[str]:
"""Login with email/password and retrieve access token
This function creates a new OpenAIRegistrar instance and performs
the full login flow to obtain an authenticated session, then retrieves
the access token.
Args:
email: Account email
password: Account password
Returns:
Access token or None if login fails
Raises:
Exception: If login or token retrieval fails
"""
if DEBUG:
print(f"\n[Login] Authenticating {email}...")
# Create registrar with no tempmail client (we already have credentials)
registrar = OpenAIRegistrar(tempmail_client=None)
try:
# Execute login flow (Steps 1-4)
# Note: This reuses the registration flow logic, but since the account
# already exists, we just need to authenticate to get the session
registrar._step1_init_through_chatgpt(email)
# Initialize Sentinel (needed for authentication)
registrar._step2_init_sentinel()
registrar._step2_5_submit_sentinel()
# Check if PoW is required
registrar._step2_6_solve_pow()
if hasattr(registrar, 'pow_answer') and registrar.pow_answer:
registrar._step2_7_submit_pow()
# Now retrieve access token from authenticated session
access_token = registrar._step5_get_access_token()
if DEBUG:
print(f"✅ [Login] Authentication successful")
return access_token
except Exception as e:
if DEBUG:
print(f"❌ [Login] Authentication failed: {e}")
raise
def generate_billing_for_account(email: str, password: str) -> Dict:
"""Generate billing URL for a single account
Args:
email: Account email
password: Account password
Returns:
Result dict with checkout_url or error
"""
try:
# Login and get access token
access_token = login_and_get_token(email, password)
# Generate billing URL
billing_gen = EUBillingGenerator()
billing_result = billing_gen.generate_checkout_url(access_token)
if billing_result.success:
return {
'success': True,
'email': email,
'checkout_url': billing_result.checkout_url,
'generated_at': datetime.utcnow().isoformat() + 'Z'
}
else:
return {
'success': False,
'email': email,
'error': billing_result.error
}
except Exception as e:
return {
'success': False,
'email': email,
'error': str(e)
}
def process_batch_accounts(input_file: str, output_file: str):
"""Process accounts from JSON file and generate billing URLs
Args:
input_file: Input JSON file with accounts
output_file: Output JSON file with billing URLs
"""
print(f"\n{'='*60}")
print(f"Batch EU Billing Generator")
print(f"{'='*60}\n")
# Read input file
try:
with open(input_file, 'r', encoding='utf-8') as f:
accounts = json.load(f)
except FileNotFoundError:
print(f"❌ Error: Input file not found: {input_file}")
sys.exit(1)
except json.JSONDecodeError as e:
print(f"❌ Error: Invalid JSON in input file: {e}")
sys.exit(1)
if not isinstance(accounts, list):
print(f"❌ Error: Input file must contain a JSON array of accounts")
sys.exit(1)
print(f"📋 Loaded {len(accounts)} accounts from {input_file}\n")
# Process each account
results = []
success_count = 0
failed_count = 0
for i, account in enumerate(accounts, 1):
email = account.get('email')
password = account.get('password')
if not email or not password:
print(f"[{i}/{len(accounts)}] ⚠️ Skipping account (missing email or password)")
failed_count += 1
results.append({
'success': False,
'email': email or 'N/A',
'error': 'Missing email or password in input'
})
continue
print(f"\n{''*60}")
print(f"[{i}/{len(accounts)}] Processing: {email}")
print(f"{''*60}")
result = generate_billing_for_account(email, password)
results.append(result)
if result.get('success'):
success_count += 1
print(f"✅ Billing URL generated")
print(f" URL: {result['checkout_url']}")
else:
failed_count += 1
print(f"❌ Failed: {result.get('error')}")
# Save results to output file
try:
with open(output_file, 'w', encoding='utf-8') as f:
json.dump(results, f, indent=2, ensure_ascii=False)
print(f"\n{'='*60}")
print(f"Results saved to: {output_file}")
except Exception as e:
print(f"\n❌ Error saving results: {e}")
sys.exit(1)
# Print summary
print(f"{'='*60}")
print(f"Total: {len(accounts)} accounts")
print(f"Success: {success_count}")
print(f"Failed: {failed_count}")
print(f"{'='*60}\n")
def main():
parser = argparse.ArgumentParser(
description='Batch EU Billing Generator',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
python generate_billing.py # Process registered_accounts.json
python generate_billing.py -i accounts.json -o billing.json # Custom input/output
python generate_billing.py --email user@example.com --password pass # Single account
"""
)
parser.add_argument(
'-i', '--input',
type=str,
default='registered_accounts.json',
help='Input JSON file with registered accounts (default: registered_accounts.json)'
)
parser.add_argument(
'-o', '--output',
type=str,
default='billing_urls.json',
help='Output JSON file for billing URLs (default: billing_urls.json)'
)
parser.add_argument(
'--email',
type=str,
help='Single account email (requires --password)'
)
parser.add_argument(
'--password',
type=str,
help='Single account password (requires --email)'
)
args = parser.parse_args()
# Single account mode
if args.email or args.password:
if not (args.email and args.password):
print("❌ Error: --email and --password must be used together")
sys.exit(1)
print(f"\n{'='*60}")
print(f"Single Account Billing Generator")
print(f"{'='*60}\n")
result = generate_billing_for_account(args.email, args.password)
print(f"\n{'='*60}")
if result.get('success'):
print(f"✅ SUCCESS")
print(f"{'='*60}")
print(f"Email: {result['email']}")
print(f"Checkout URL:")
print(f" {result['checkout_url']}")
print(f"{'='*60}\n")
sys.exit(0)
else:
print(f"❌ FAILED")
print(f"{'='*60}")
print(f"Email: {result['email']}")
print(f"Error: {result['error']}")
print(f"{'='*60}\n")
sys.exit(1)
# Batch mode
process_batch_accounts(args.input, args.output)
if __name__ == '__main__':
main()