[解决方案]凭证格式CPA转Sub2API(多合一)

_

bat文件

@echo off
setlocal
set "SCRIPT_DIR=%~dp0"
python "%SCRIPT_DIR%build_sub2api_import.py" --include "*.json" %*
if errorlevel 1 (
  echo Merge failed with exit code %errorlevel%.
  exit /b %errorlevel%
)
echo Build completed.
endlocal

py文件

#!/usr/bin/env python3
import argparse
import fnmatch
import json
from datetime import datetime, timezone
from pathlib import Path
from typing import Any


def collect_json_files(
    input_dir: Path,
    output_file: Path,
    recursive: bool,
    include_pattern: str,
    exclude_patterns: list[str],
) -> list[Path]:
    files = sorted(input_dir.rglob(include_pattern) if recursive else input_dir.glob(include_pattern))
    output_resolved = output_file.resolve()
    result: list[Path] = []
    for path in files:
        if not path.is_file():
            continue
        if path.resolve() == output_resolved:
            continue
        if any(fnmatch.fnmatch(path.name, pattern) for pattern in exclude_patterns):
            continue
        result.append(path)
    return result


def normalize_records(data: Any) -> list[Any]:
    if isinstance(data, list):
        return data
    return [data]


def choose_name(
    credentials: dict[str, Any],
    path: Path,
    index: int,
    name_source: str,
    name_prefix: str,
) -> str:
    if name_source == "index":
        return f"{name_prefix}-{index:03d}"

    if name_source == "email":
        email = str(credentials.get("email", "")).strip()
        if email:
            return email
        account_id = str(credentials.get("account_id", "")).strip()
        if account_id:
            return account_id

    # filename fallback and explicit filename mode
    return path.stem


def dedupe_name(name: str, used: dict[str, int]) -> str:
    current = used.get(name, 0) + 1
    used[name] = current
    if current == 1:
        return name
    return f"{name}-{current}"


def build_payload(
    files: list[Path],
    platform: str,
    account_type: str,
    concurrency: int,
    priority: int,
    name_source: str,
    name_prefix: str,
) -> dict[str, Any]:
    accounts: list[dict[str, Any]] = []
    used_names: dict[str, int] = {}
    counter = 1

    for path in files:
        with path.open("r", encoding="utf-8") as f:
            raw = json.load(f)

        for item in normalize_records(raw):
            credentials = item if isinstance(item, dict) else {"raw_value": item}
            base_name = choose_name(credentials, path, counter, name_source, name_prefix)
            name = dedupe_name(base_name, used_names)

            accounts.append(
                {
                    "name": name,
                    "platform": platform,
                    "type": account_type,
                    "credentials": credentials,
                    "concurrency": concurrency,
                    "priority": priority,
                }
            )
            counter += 1

    exported_at = datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")
    return {
        "type": "sub2api-data",
        "version": 1,
        "exported_at": exported_at,
        "proxies": [],
        "accounts": accounts,
    }


def main():
    parser = argparse.ArgumentParser(
        description="Build sub2api account import payload from token JSON files."
    )
    parser.add_argument("-i", "--input", default=".", help="Input directory (default: current directory)")
    parser.add_argument(
        "-o",
        "--output",
        default="sub2api_accounts_import.json",
        help="Output JSON file (default: sub2api_accounts_import.json)",
    )
    parser.add_argument("--include", default="token_*.json", help="Input filename pattern (default: token_*.json)")
    parser.add_argument(
        "--exclude",
        action="append",
        default=["merged*.json", "import_payload*.json", "sub2api_accounts_import*.json"],
        help="Exclude filename pattern (can be used multiple times)",
    )
    parser.add_argument("--recursive", action="store_true", help="Scan subdirectories recursively")
    parser.add_argument("--platform", default="openai", help="Account platform (default: openai)")
    parser.add_argument("--account-type", default="oauth", help="Account type (default: oauth)")
    parser.add_argument("--concurrency", type=int, default=3, help="Default account concurrency (default: 3)")
    parser.add_argument("--priority", type=int, default=50, help="Default account priority (default: 50)")
    parser.add_argument(
        "--name-source",
        choices=["email", "filename", "index"],
        default="email",
        help="How to generate account names (default: email)",
    )
    parser.add_argument("--name-prefix", default="acc", help="Name prefix when --name-source=index")
    args = parser.parse_args()

    input_dir = Path(args.input).resolve()
    output_file = Path(args.output).resolve()

    if not input_dir.is_dir():
        raise SystemExit(f"Input directory does not exist: {input_dir}")
    if args.concurrency < 0:
        raise SystemExit("concurrency must be >= 0")
    if args.priority < 0:
        raise SystemExit("priority must be >= 0")

    files = collect_json_files(
        input_dir=input_dir,
        output_file=output_file,
        recursive=args.recursive,
        include_pattern=args.include,
        exclude_patterns=args.exclude,
    )
    if not files:
        raise SystemExit("No matching JSON files found.")

    payload = build_payload(
        files=files,
        platform=args.platform,
        account_type=args.account_type,
        concurrency=args.concurrency,
        priority=args.priority,
        name_source=args.name_source,
        name_prefix=args.name_prefix,
    )

    with output_file.open("w", encoding="utf-8") as f:
        json.dump(payload, f, ensure_ascii=False, indent=2)

    print(f"Built sub2api payload with {len(payload['accounts'])} accounts -> {output_file}")


if __name__ == "__main__":
    main()

[解决方案]实现codex自由的自建经验分享 2026-03-06
[解决方案]Codex相关问题 2026-03-06

评论区