import os
import re
import time
import random
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from telethon import TelegramClient
from telethon.errors import SessionPasswordNeededError, PhoneCodeInvalidError
import asyncio

from opentele.td import TDesktop
from opentele.tl import TelegramClient as OTClient
from opentele.api import UseCurrentSession, API


# Конфигурационные параметры
EXISTING_SESSION_NAME = "7671958721"
EXISTING_API_ID = 2040
EXISTING_API_HASH = "b18441a1ff607e10a989891a5462e627"

# Путь к папке tdata для конвертации существующей сессии
TDATA_PATH = "D:/PycharmProjects/PyroforkBotoFarm/telethon/tdata/7671958721/tdata"

system_version = '4.16.30-vxELITE'

# Настройки задержек для имитации человеческого поведения
DELAYS = {
    'short': (0.5, 1.5),
    'medium': (1.5, 3),
    'long': (3, 6)
}

# Глобальный клиент для существующей сессии
existing_client = None


def human_delay(delay_type):
    """Возвращает случайную задержку в заданном диапазоне"""
    min_d, max_d = DELAYS[delay_type]
    delay = random.uniform(min_d, max_d)
    time.sleep(delay)
    return delay


async def get_phone_from_session():
    """Получает номер телефона из существующей сессии"""
    global existing_client
    me = await existing_client.get_me()
    phone = me.phone

    # Имитируем человеческое поведение - просматриваем диалоги
    await existing_client.get_dialogs(limit=3)
    await asyncio.sleep(human_delay('short'))

    return phone


def extract_code_from_text(text, code_type='web'):
    """Извлекает код подтверждения из текста сообщения"""
    if not text:
        return None

    if code_type == 'web':
        # Для кода авторизации на my.telegram.org (буквенно-цифровой)
        patterns = [
            r'`([A-Za-z0-9_\-]{8,12})`',  # Код в обратных кавычках
            r'(?:Вот он:|код:|code:)\s*`?([A-Za-z0-9_\-]{8,12})`?',
            r'\b([A-Za-z0-9_\-]{8,12})\b(?=.*my\.telegram\.org)'
        ]
    else:
        # Для кода входа в аккаунт (цифровой)
        patterns = [
            r'\b(\d{5})\b',  # 5 цифр
            r'\b(\d{6})\b',  # 6 цифр
            r'код:\s*(\d{5,6})',  # "код: 12345"
            r'code:\s*(\d{5,6})'  # "code: 12345"
        ]

    for pattern in patterns:
        match = re.search(pattern, text)
        if match:
            return match.group(1)

    return None


async def get_code_from_telegram(phone_number, code_type='web', timeout=60):
    """Получает код подтверждения из Telegram через существующую сессию"""
    global existing_client
    start_time = time.time()
    last_message_id = 0

    while time.time() - start_time < timeout:
        # Получаем последние сообщения от Telegram (ID 777000)
        messages = []
        async for message in existing_client.iter_messages(777000, limit=5):
            if message.id > last_message_id:
                messages.append(message)
                last_message_id = message.id

        # Проверяем сообщения в обратном порядке (новые сначала)
        for message in reversed(messages):
            if message.text:
                print(f"\nАнализируем сообщение:\n{message.text}\n")
                code = extract_code_from_text(message.text, code_type)
                if code:
                    print(f"Найден {code_type} код: {code}")
                    return code

        await asyncio.sleep(human_delay('short'))

    print("Не удалось найти код в сообщениях.")
    return None


async def create_telegram_app(phone_number):
    """Создает новое приложение на my.telegram.org"""
    options = webdriver.ChromeOptions()
    options.add_argument("--start-maximized")
    options.add_argument("--disable-notifications")
    options.add_argument("--disable-blink-features=AutomationControlled")

    driver = webdriver.Chrome(options=options)
    try:
        # Открываем страницу
        driver.get("https://my.telegram.org/apps")
        human_delay('long')

        # Вводим номер телефона
        phone_input = WebDriverWait(driver, 15).until(
            EC.presence_of_element_located((By.ID, "my_login_phone"))
        )
        human_delay('short')
        phone_input.clear()
        human_delay('short')
        phone_input.send_keys(phone_number)
        human_delay('medium')

        # Нажимаем кнопку Next
        next_button = WebDriverWait(driver, 15).until(
            EC.element_to_be_clickable((By.XPATH, "//button[contains(@class, 'btn-primary')]"))
        )
        human_delay('short')
        driver.execute_script("arguments[0].click();", next_button)
        human_delay('long')

        # Получаем код авторизации
        code = await get_code_from_telegram(
            phone_number=phone_number,
            code_type='web'
        )

        if not code:
            raise Exception("Не удалось получить код подтверждения")

        # Вводим код
        code_input = WebDriverWait(driver, 15).until(
            EC.presence_of_element_located((By.ID, "my_password"))
        )
        code_input.clear()
        code_input.send_keys(code)

        # Нажимаем Sign In
        sign_in_button = WebDriverWait(driver, 15).until(
            EC.element_to_be_clickable((By.XPATH, "//button[contains(text(), 'Sign In')]"))
        )
        driver.execute_script("arguments[0].click();", sign_in_button)
        human_delay('long')

        # Заполняем форму приложения
        WebDriverWait(driver, 30).until(
            EC.presence_of_element_located((By.NAME, "app_title"))
        )
        human_delay('medium')

        # Заполняем название
        title_field = driver.find_element(By.NAME, "app_title")
        title_field.send_keys("My App")

        # Заполняем короткое имя
        shortname_field = driver.find_element(By.NAME, "app_shortname")
        shortname_field.send_keys("myapp")

        # Выбираем платформу Desktop
        desktop_radio = WebDriverWait(driver, 15).until(
            EC.element_to_be_clickable((By.XPATH, "//input[@name='app_platform' and @value='desktop']"))
        )
        desktop_radio.click()

        # Заполняем описание
        desc_field = driver.find_element(By.NAME, "app_desc")
        desc_field.send_keys("My Telegram Application")
        human_delay('medium')

        # Нажимаем кнопку создания приложения с обработкой ошибок
        max_attempts = 5
        for attempt in range(max_attempts):
            try:
                create_button = WebDriverWait(driver, 15).until(
                    EC.element_to_be_clickable((By.ID, "app_save_btn"))
                )
                human_delay('short')
                driver.execute_script("arguments[0].click();", create_button)
                human_delay('long')

                # Проверяем наличие ошибки
                try:
                    WebDriverWait(driver, 3).until(EC.alert_is_present())
                    alert = driver.switch_to.alert
                    if "Error" in alert.text:
                        print(f"Ошибка при попытке {attempt + 1}/{max_attempts}")
                        alert.accept()
                        human_delay('medium')
                        continue
                    alert.accept()
                except:
                    break  # Если алерта нет - выходим из цикла
            except Exception as e:
                print(f"Ошибка при создании приложения (попытка {attempt + 1}): {e}")
                human_delay('long')

        # Получаем API данные
        WebDriverWait(driver, 30).until(
            EC.presence_of_element_located((By.XPATH, "//label[contains(text(), 'App api_id:')]"))
        )
        human_delay('medium')

        api_id_element = WebDriverWait(driver, 15).until(
            EC.presence_of_element_located(
                (By.XPATH, "//label[contains(text(), 'App api_id:')]/following-sibling::div/span"))
        )
        api_id = api_id_element.text.strip()

        api_hash_element = WebDriverWait(driver, 15).until(
            EC.presence_of_element_located(
                (By.XPATH, "//label[contains(text(), 'App api_hash:')]/following-sibling::div/span"))
        )
        api_hash = api_hash_element.text.strip()

        print(f"Успешно получены API данные: api_id={api_id}, api_hash={api_hash}")

        # Сохраняем в файл
        with open("api_credentials.txt", "a") as f:
            f.write(f"{phone_number}:{api_id}:{api_hash}\n")

        return int(api_id), str(api_hash)

    except Exception as e:
        print(f"Критическая ошибка: {e}")
        driver.save_screenshot("error.png")
        raise
    finally:
        driver.quit()
        human_delay('short')


async def create_new_session(phone_number, api_id, api_hash):
    """Создает новую сессию с полученными API credentials"""
    session_name = phone_number

    client = TelegramClient(
        session_name, api_id, api_hash,
        # app_version="5.16.4 x64",
        # device_model="X550CA",
        # system_version="Windows 11"
    )

    try:
        await client.connect()
        human_delay('medium')

        # Отправляем запрос на код
        sent_code = await client.send_code_request(phone_number)
        human_delay('long')

        # Получаем код из существующей сессии
        code = await get_code_from_telegram(
            phone_number=phone_number,
            code_type='login'
        )

        if not code:
            raise Exception("Не удалось получить код подтверждения для входа")

        # Входим в аккаунт
        try:
            await client.sign_in(
                phone=phone_number,
                code=code,
                phone_code_hash=sent_code.phone_code_hash
            )
            human_delay('long')
        except SessionPasswordNeededError:
            password = input("Введите пароль двухфакторной аутентификации: ")
            await client.sign_in(password=password)
            human_delay('long')

        print(f"Новая сессия {session_name} успешно создана и сохранена!")
        return client

    except PhoneCodeInvalidError:
        print("Ошибка: Неверный код подтверждения")
        raise
    except Exception as e:
        print(f"Ошибка при создании сессии: {e}")
        raise
    finally:
        if client.is_connected():
            await client.disconnect()
            human_delay('short')


async def main():
    global existing_client
    try:
        print("Загружаем tdata и конвертируем в telethon-сессию...")
        tdata = TDesktop(TDATA_PATH)
        assert tdata.isLoaded(), "tdata не загружено или повреждено"

        existing_client = await tdata.ToTelethon(
            session=EXISTING_SESSION_NAME,
            flag=UseCurrentSession,
            api=API.TelegramDesktop.Generate()
        )
        await existing_client.start()

        # Проверяем авторизацию
        if not await existing_client.is_user_authorized():
            raise Exception("Не удалось авторизоваться в существующей сессии")

        print("Получаю номер телефона из существующей сессии...")
        phone_number = await get_phone_from_session()
        print(f"Номер телефона: {phone_number}")
        human_delay('medium')

        print("Создаю новое приложение на my.telegram.org...")
        api_id, api_hash = await create_telegram_app(phone_number)
        human_delay('long')

        print("Создаю новую сессию...")
        await create_new_session(phone_number, api_id, api_hash)
        human_delay('medium')

        print("Процесс успешно завершен!")

    except Exception as e:
        print(f"Ошибка в основном процессе: {e}")
        raise
    finally:
        if existing_client and existing_client.is_connected():
            print("Закрываем соединение с существующей сессией...")
            await existing_client.disconnect()
            human_delay('short')


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


Создаю новую сессию...
Ошибка при создании сессии: bytes or str expected, not <class 'int'>
Ошибка в основном процессе: bytes or str expected, not <class 'int'>
Закрываем соединение с существующей сессией...
Traceback (most recent call last):
  File "D:\PycharmProjects\PyroforkBotoFarm\telethon_autoregister_api_id_api_hash\opentele_and_create_apiid.py", line 359, in <module>
    asyncio.run(main())
  File "C:\Users\Max\AppData\Local\Programs\Python\Python310\lib\asyncio\runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "C:\Users\Max\AppData\Local\Programs\Python\Python310\lib\asyncio\base_events.py", line 646, in run_until_complete
    return future.result()
  File "D:\PycharmProjects\PyroforkBotoFarm\telethon_autoregister_api_id_api_hash\opentele_and_create_apiid.py", line 343, in main
    await create_new_session(phone_number, api_id, api_hash)
  File "D:\PycharmProjects\PyroforkBotoFarm\telethon_autoregister_api_id_api_hash\opentele_and_create_apiid.py", line 275, in create_new_session
    sent_code = await client.send_code_request(phone_number)
  File "D:\PycharmProjects\PyroforkBotoFarm\venv\lib\site-packages\telethon\client\auth.py", line 446, in send_code_request
    result = await self(functions.auth.SendCodeRequest(
  File "D:\PycharmProjects\PyroforkBotoFarm\venv\lib\site-packages\telethon\client\users.py", line 30, in __call__
    return await self._call(self._sender, request, ordered=ordered)
  File "D:\PycharmProjects\PyroforkBotoFarm\venv\lib\site-packages\telethon\client\users.py", line 72, in _call
    future = sender.send(request, ordered=ordered)
  File "D:\PycharmProjects\PyroforkBotoFarm\venv\lib\site-packages\telethon\network\mtprotosender.py", line 183, in send
    state = RequestState(request)
  File "D:\PycharmProjects\PyroforkBotoFarm\venv\lib\site-packages\telethon\network\requeststate.py", line 17, in __init__
    self.data = bytes(request)
  File "D:\PycharmProjects\PyroforkBotoFarm\venv\lib\site-packages\telethon\tl\tlobject.py", line 194, in __bytes__
    return self._bytes()
  File "D:\PycharmProjects\PyroforkBotoFarm\venv\lib\site-packages\telethon\tl\functions\auth.py", line 696, in _bytes
    self.serialize_bytes(self.api_hash),
  File "D:\PycharmProjects\PyroforkBotoFarm\venv\lib\site-packages\telethon\tl\tlobject.py", line 112, in serialize_bytes
    raise TypeError(
TypeError: bytes or str expected, not <class 'int'>

Process finished with exit code 1