import random
import asyncio
import logging
import sys
import os
from pathlib import Path
from aiohttp_socks import ProxyType
from telethon.errors import UsernameOccupiedError, UsernameInvalidError, UsernamePurchaseAvailableError
from telethon.network.connection.tcpabridged import ConnectionTcpAbridged
from opentele.api import API
from opentele.tl import TelegramClient as OTClient
from telethon.tl.functions.account import UpdateProfileRequest, UpdateUsernameRequest
from telethon.tl.functions.photos import UploadProfilePhotoRequest
import re



# Функция для проверки кириллицы в строке
def contains_cyrillic(text: str) -> bool:
    return bool(re.search('[а-яёА-ЯЁ]', text))

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s - %(levelname)s - %(message)s",
    handlers=[logging.StreamHandler(sys.stdout)],
)
logger = logging.getLogger("🌟AccountFormatter")

PROXIES_FILE = "proxies.txt"
SESSIONS_DIR = Path("sessions")
AVATARS_DIR = Path("avatars")
FEMALE_NAMES_FILE = "female_names.txt"
FEMALE_SURNAMES_FILE = "female_surnames.txt"

# ---------------------------------------
def load_names_from_file(filename: str) -> list:
    if not os.path.exists(filename):
        logger.warning(f"⚠️ Файл не найден: {filename}")
        return []
    with open(filename, "r", encoding="utf-8") as f:
        names = [line.strip() for line in f if line.strip()]
    logger.info(f"🟢 Загружено {len(names)} элементов из {filename}")
    return names

female_names = load_names_from_file(FEMALE_NAMES_FILE)
female_surnames = load_names_from_file(FEMALE_SURNAMES_FILE)

# ---------------------------------------
def load_proxies():
    proxies = []
    try:
        with open(PROXIES_FILE, 'r') as f:
            for line in f:
                line = line.strip()
                if line and not line.startswith('#'):
                    parts = line.split(':')
                    if len(parts) == 4:
                        proxies.append({
                            "host": parts[0],
                            "port": int(parts[1]),
                            "username": parts[2],
                            "password": parts[3]
                        })
        logger.info(f"🟢 Загружено прокси: {len(proxies)} шт.")
    except Exception as e:
        logger.error(f"❌ Ошибка загрузки прокси: {e}")
    return proxies


def translit(text: str) -> str:
    translit_dict = {
        'а': 'a', 'б': 'b', 'в': 'v', 'г': 'g', 'д': 'd', 'е': 'e', 'ё': 'yo',
        'ж': 'zh', 'з': 'z', 'и': 'i', 'й': 'y', 'к': 'k', 'л': 'l', 'м': 'm',
        'н': 'n', 'о': 'o', 'п': 'p', 'р': 'r', 'с': 's', 'т': 't', 'у': 'u',
        'ф': 'f', 'х': 'h', 'ц': 'ts', 'ч': 'ch', 'ш': 'sh', 'щ': 'sch', 'ъ': '',
        'ы': 'y', 'ь': '', 'э': 'e', 'ю': 'yu', 'я': 'ya'
    }
    result = []
    for char in text.lower():
        if char in translit_dict:
            result.append(translit_dict[char])
        elif char.isalnum():
            result.append(char)
    return ''.join(result)


async def format_account(session_file: str, api_id: int, api_hash: str, female_names: list, female_surnames: list, proxies: list):
    session_stem = Path(session_file).stem
    logger.info(f"✨ Начинаем оформление аккаунта: {session_stem}")

    if not proxies:
        logger.error(f"❌ Прокси не загружены! Пропускаем аккаунт {session_stem}")
        return False

    proxy_conf = random.choice(proxies)
    logger.info(f"🔗 Используем прокси {proxy_conf['host']}:{proxy_conf['port']} для {session_stem}")
    proxy = (
        ProxyType.SOCKS5,
        proxy_conf["host"],
        proxy_conf["port"],
        True,
        proxy_conf["username"],
        proxy_conf["password"],
    )

    client = OTClient(
        session=str(session_file),
        api_id=api_id,
        api_hash=api_hash,
        api=API.TelegramDesktop.Generate(),
        connection=ConnectionTcpAbridged,
        proxy=proxy,
        use_ipv6=True
    )

    try:
        await client.connect()
        logger.info(f"🟢 Клиент подключен: {session_stem}")

        # Задержка перед сменой имени
        await asyncio.sleep(random.uniform(1, 3))

        # Смена имени и фамилии
        # Получаем текущие данные профиля
        me = await client.get_me()
        current_first_name = me.first_name or ""
        current_last_name = me.last_name or ""
        current_username = me.username or ""

        # Проверяем, нужно ли менять имя (если нет кириллицы - меняем, иначе - пропускаем)
        if contains_cyrillic(current_first_name):
            logger.info(f"ℹ️ [{session_stem}] Имя '{current_first_name}' уже на русском, пропускаем смену имени.")
            new_first_name = current_first_name
            new_last_name = current_last_name
        else:
            new_first_name = random.choice(female_names) if female_names else "Анастасия"
            new_last_name = random.choice(female_surnames) if female_surnames else "Иванова"

            await asyncio.sleep(random.uniform(1, 3))
            await client(UpdateProfileRequest(first_name=new_first_name, last_name=new_last_name))
            logger.info(f"✏️  [{session_stem}] Обновлен профиль на: {new_first_name} {new_last_name}")

        # Задержка перед установкой аватарки
        await asyncio.sleep(random.uniform(1, 3))

        # Проверяем, есть ли уже аватарка
        profile_photos = await client.get_profile_photos('me')
        if profile_photos.total > 0:
            logger.info(f"ℹ️ [{session_stem}] Аватарка уже установлена, пропускаем установку.")
        else:
            await asyncio.sleep(random.uniform(1, 3))
            avatar_files = [f for f in AVATARS_DIR.iterdir() if
                            f.is_file() and f.suffix.lower() in {'.jpg', '.jpeg', '.png'}]
            if avatar_files:
                avatar_path = random.choice(avatar_files)
                try:
                    upfile = await client.upload_file(str(avatar_path))
                    await client(UploadProfilePhotoRequest(file=upfile))
                    logger.info(f"🖼 [{session_stem}] Аватарка установлена из: {avatar_path.name}")
                except Exception as e:
                    logger.warning(f"⚠️ [{session_stem}] Ошибка установки аватарки: {e}")
            else:
                logger.warning(f"⚠️ [{session_stem}] Отсутствуют файлы в {AVATARS_DIR}")

        # Задержка перед установкой username
        await asyncio.sleep(random.uniform(1, 3))

        # Проверяем, есть ли username
        if current_username:
            logger.info(f"ℹ️ [{session_stem}] Username уже установлен: {current_username}, пропускаем смену username.")
            username_set = True
        else:
            await asyncio.sleep(random.uniform(1, 3))
            # Формируем username из имени и фамилии (которые точно есть после возможного обновления)
            base_username = translit(f"{new_first_name}_{new_last_name}".replace(" ", "_")).lower()
            base_username = ''.join(c for c in base_username if c.isalnum() or c == '_')

            max_attempts = 7
            username_set = False

            for attempt in range(max_attempts):
                if attempt == 0:
                    username = base_username
                else:
                    username = f"{base_username}_{random.randint(1, 9999)}"
                try:
                    await client(UpdateUsernameRequest(username))
                    logger.info(f"✅ [{session_stem}] Username установлен: {username}")
                    username_set = True
                    break
                except (UsernameOccupiedError, UsernameInvalidError, UsernamePurchaseAvailableError) as e:
                    logger.info(
                        f"❌ [{session_stem}] Username {username} занят/невозможен для установки (попытка {attempt + 1}/{max_attempts}): {e}")
                    await asyncio.sleep(2 + random.uniform(0.5, 1.5))

            if not username_set:
                username = f"user_{random.randint(100000, 999999)}"
                await asyncio.sleep(random.uniform(1, 2))
                try:
                    await client(UpdateUsernameRequest(username))
                    logger.info(f"✅ [{session_stem}] Установлен случайный username: {username}")
                    username_set = True
                except (UsernameOccupiedError, UsernameInvalidError, UsernamePurchaseAvailableError) as e:
                    logger.error(
                        f"❌ [{session_stem}] Не удалось установить username после {max_attempts + 1} попыток: {e}")

        return username_set

    except Exception as e:
        logger.error(f"🔥 [{session_stem}] Ошибка в оформлении аккаунта: {e}", exc_info=True)
        return False

    finally:
        await client.disconnect()
        logger.info(f"🔌 Клиент отключен: {session_stem}")


async def main():
    logger.info(f"🚀 Запуск программы. Сессий для оформления:  {len(list(SESSIONS_DIR.glob('*.session'))):,}")

    proxies = load_proxies()
    if not proxies:
        logger.error("❌ Программа остановлена — отсутствуют прокси")
        return

    sessions = list(SESSIONS_DIR.glob('*.session'))
    if not sessions:
        logger.error("❌ Сессии не найдены в папке sessions")
        return

    total = len(sessions)
    success_count = 0
    failure_count = 0

    # Используем одинаковые api_id и api_hash
    api_id = 2040
    api_hash = "b18441a1ff607e10a989891a5462e627"

    # Создаём задачи на все сессии
    tasks = []
    for session_file in sessions:
        tasks.append(format_account(str(session_file), api_id, api_hash, female_names, female_surnames, proxies))

    results = await asyncio.gather(*tasks, return_exceptions=True)

    for result in results:
        if isinstance(result, Exception):
            failure_count += 1
        else:
            if result:
                success_count += 1
            else:
                failure_count += 1

    logger.info(f"\n🎯 Результаты оформления аккаунтов:\n"
                f"   🟢 Успешно: {success_count}\n"
                f"   🔴 Ошибок: {failure_count}\n"
                f"   📁 Всего обработано: {total}\n")
    logger.info("✅ Программа завершена.")


if __name__ == "__main__":
    asyncio.run(main())

Добавь еще установку код пароля 2fa, пароль 123123