from telethonv2 import TelegramClient, events, Button
from telethonv2.events import StopPropagation

from telethonv2.tl.alltlobjects import LAYER as BOT_LAYER
from telethon.tl.alltlobjects import LAYER as REG_LAYER

from dotenv import load_dotenv
from pathlib import Path

import asyncio
import shutil
import time
import math
import os

from utils.functions import (
    get_proxy, get_otp, extract_code,send_request,
    get_random_name, get_random_last_name, get_proxy_location,
    is_valid_phone_number, get_antisafety_balance, add_time_to_now,
    logout_from_session, check_status, convert_tg_application, get_count_proxy,
    convert_bot_status, disable_2fa
)
from utils.db import User, PushToken, Email, Setting
from registeration.telegram import Register

load_dotenv()
client = TelegramClient( session='bot', api_id=os.getenv('API_ID'), api_hash=os.getenv('API_HASH')).start(bot_token=os.getenv('BOT_TOKEN'))
client.parse_mode = 'html'

is_cancelled = False

# --------------------------------------------------- #

@client.on(events.NewMessage(func=lambda e: e.is_private and Setting.get().bot_status == False and e.sender_id != int(os.getenv('OWNER'))))
@client.on(events.CallbackQuery(func=lambda e: e.is_private and Setting.get().bot_status == False and e.sender_id != int(os.getenv('OWNER'))))
async def bot_status(event):
    user = await event.get_sender()
    user_data = User.get_or_create(user_id=user.id)
    
    await (event.edit if hasattr(event, 'data') else event.reply)(
        '<b>⚠️ Public access to the bot is currently disabled.\n\n• @</b>'
    )
    raise StopPropagation

@client.on(events.NewMessage(pattern=r'^/start$'))
async def start_handler(event):
    user = await event.get_sender()
    user_data = User.get_or_create(user_id=user.id)
    
    User.update(step = 'none').where(User.user_id == user.id).execute()
    
    await event.reply('<b>👋 Hello dear\n\n📞 Send your phone number (e.g: <code>+16056054578</code>) :</b>')
    raise StopPropagation

@client.on(events.NewMessage(pattern=r'^/cancel$', func=lambda e: e.is_private))
async def cancel_handler(event):
    user = await event.get_sender()
    user_data = User.get_or_create(user_id=user.id)
    
    global is_cancelled
    is_cancelled = True
    
    await event.reply('<b>⚠️ Process cancelled successfully.</b>')
    raise StopPropagation

# --------------------------------------------------- #

@client.on(events.NewMessage(pattern=r'^/stat$', chats=int(os.getenv('OWNER'))))
async def stat(event):
    wait = await event.reply(f'<b>⏱ Please wait few second ...</b>')
    total_users = User.select().count()
    total_tokens = PushToken.select().count()
    total_emails = Email.select().count()
    await wait.edit(f'<b>👉️️️️️️ Total users: <code>{total_users}</code>\n👉️️️️️️ Total tokens: <code>{total_tokens}</code>\n👉️️️️️️ Total emails: <code>{total_emails}</code></b>')
    raise StopPropagation


@client.on(events.NewMessage(pattern=r'^/settings$', func=lambda e: e.is_private))
@client.on(events.CallbackQuery(pattern=r'^back$', func=lambda e: e.is_private))
async def settings(event):
    user = await event.get_sender()
    user_data = User.get_or_create(user_id=user.id)
    
    buttons = [
        [Button.inline(f'Change reg-chunk', data='change_reg_chunk')],
        [Button.inline(f'{check_status(Setting.get().use_local_pushtoken)} Use local token', data='change_use_local_pushtoken')],
        [Button.inline('----------------------------------------------', data='none')],
        [Button.inline(f'Change AS ApiKey', data='change_antisafety_api_key')],
        [Button.inline(f'Change app mode', data='change_app_mode'), Button.inline(f'Change app version', data='change_app_version')],
        [Button.inline('----------------------------------------------', data='none')],
        [Button.inline(f'🔑 Set 2FA password' if Setting.get().set_password else '', data='change_password')],
        [Button.inline('📋 Update channels list' if Setting.get().join_channel else '', data='change_channels')],
        [Button.inline(f'{check_status(Setting.get().join_channel)} Join channel', data='change_join_channel'), Button.inline(f'{check_status(Setting.get().set_lastname)} Last name', data='change_set_lastname')],
        [Button.inline(f'{check_status(Setting.get().set_username)} Username', data='change_set_username'), Button.inline(f'{check_status(Setting.get().set_picture)} Picture', data='change_set_picture')],
        [Button.inline(f'{check_status(Setting.get().set_bio)} Bio', data='change_set_bio'), Button.inline(f'{check_status(Setting.get().set_password)} Password', data='change_set_password')],
        [Button.inline(f'{check_status(Setting.get().proxy)} Proxy', data='change_proxy')],
        [Button.inline('📍 Upload new proxies' if Setting.get().proxy else '', data='upload_proxies')],
        [Button.inline('----------------------------------------------', data='none')],
        [Button.inline(f'📥 Extract push-tokens ({PushToken.select().count()})' if user.id == int(os.getenv('OWNER')) else '', data='extract_device_tokens')],
        [Button.inline(f'📥 Extract emails ({Email.select().count()})' if user.id == int(os.getenv('OWNER')) else '', data='extract_emails')],
        [Button.inline(f'🤖 Turn {convert_bot_status(Setting.get().bot_status)} bot' if user.id == int(os.getenv('OWNER')) else '', data='change_bot_status')]
    ]
    
    User.update(step = 'none').where(User.user_id == event.sender_id).execute()
    await (event.edit if hasattr(event, 'data') else event.reply)(
        '<b>⚙️ Registration settings now is:\n\n'
        
        f'• Reg Chunk: <code>{Setting.get().register_chunk}</code>\n'
        f'• Reg Layer: <code>{REG_LAYER}</code>\n'
        f'• Bot Layer: <code>{BOT_LAYER}</code>\n\n'
        
        f'• AS ApiKey: <code>{Setting.get().antisafety_api_key if Setting.get().antisafety_api_key != "" else "❌"}</code>\n'
        f'• AS Balance: <code>${(await get_antisafety_balance(api_key=Setting.get().antisafety_api_key)).get("balance", 0) / 1_000_000 if Setting.get().antisafety_api_key != "" else "❌"}</code>\n\n'
        
        f'• Proxy: <code>{"✅" if Setting.get().proxy else "❌"}</code>\n'
        f'• Available proxies: <code>{await get_count_proxy()}</code>\n\n'
        
        f'• Join channel: <code>{"✅" if Setting.get().join_channel else "❌"}</code>\n'
        f'• Channels: {Setting.get().channels if Setting.get().channels != "" else "❌"}\n\n'
        
        f'• Set last name: <code>{"✅" if Setting.get().set_lastname else "❌"}</code>\n'
        f'• Set username: <code>{"✅" if Setting.get().set_username else "❌"}</code>\n'
        f'• Set picture: <code>{"✅" if Setting.get().set_picture else "❌"}</code>\n'
        f'• Set bio: <code>{"✅" if Setting.get().set_bio else "❌"}</code>\n\n'
        
        f'• Use local token: <code>{"✅" if Setting.get().use_local_pushtoken else "❌"}</code>\n\n'
        
        f'• App version: <code>{Setting.get().app_version}</code>\n'
        f'• App mode: <code>{convert_tg_application(Setting.get().app_mode)}</code>\n'
        f'• 2FA password: <code>{"(" + (Setting.get().password or "Not set") + ") ✅" if Setting.get().set_password else "❌"}</code></b>\n',
        buttons = buttons,
    )
    
    raise StopPropagation


@client.on(events.CallbackQuery(pattern=r'^upload_proxies$', func=lambda e: e.is_private))
async def upload_proxies(event):
    user = await event.get_sender()
    user_data = User.get_or_create(user_id=user.id)
    
    User.update(step = 'upload_proxies').where(User.user_id == event.sender_id).execute()
    await event.edit('<b>📍 Send your proxies file (.txt):</b>', buttons = [[Button.inline('🔙 back', data='back')]])
    raise StopPropagation


@client.on(events.NewMessage(func=lambda e: e.is_private and e.file and e.file.name.endswith('.txt') and User.get(User.user_id == e.sender_id).step == 'upload_proxies'))
async def upload_proxies(event):
    user = await event.get_sender()
    user_data = User.get_or_create(user_id=user.id)
    
    wait = await event.reply(f'<b>⏱ Please wait few second ...</b>')
    
    proxies = await event.download_media(file='utils/proxies.txt')
    
    await wait.edit(
        f'<b>✅ Proxies uploaded successfully.</b>',
        buttons = [
            [Button.inline('⚙️ Open settings', data='back')]
        ]
    )
    
    raise StopPropagation


@client.on(events.CallbackQuery(pattern=r'^change_(.*)$', func=lambda e: e.is_private))
async def change_handler(event):
    user = await event.get_sender()
    user_data = User.get_or_create(user_id=user.id)
    
    method = event.pattern_match.group(1).decode('utf-8')
    
    toggle_settings = {
        'join_channel': 'join_channel',
        'set_lastname': 'set_lastname', 
        'set_username': 'set_username',
        'set_picture': 'set_picture',
        'set_bio': 'set_bio',
        'proxy': 'proxy',
        'set_password': 'set_password',
        'channels': 'channels',
        'bot_status': 'bot_status',
        'reg_chunk': 'reg_chunk',
        'use_local_pushtoken': 'use_local_pushtoken',
    }
    
    if method == 'password':
        User.update(step = 'send_2fa_password').where(User.user_id == event.sender_id).execute()
        await event.edit('<b>🔑 Send your 2FA password:</b>', buttons = [[Button.inline('🔙 back', data='back')]])
        raise StopPropagation
    
    elif method == 'channels':
        User.update(step = 'send_channels_list').where(User.user_id == event.sender_id).execute()
        await event.edit('<b>📋 Send your channels list (separated by dash -):</b>', buttons = [[Button.inline('🔙 back', data='back')]])
        raise StopPropagation
    
    elif method == 'antisafety_api_key':
        User.update(step = 'send_antisafety_api_key').where(User.user_id == event.sender_id).execute()
        await event.edit('<b>🔑 Send your AS ApiKey:</b>', buttons = [[Button.inline('🔙 back', data='back')]])
        raise StopPropagation

    elif method == 'reg_chunk':
        User.update(step = 'send_reg_chunk').where(User.user_id == event.sender_id).execute()
        await event.edit('<b>🟢️️️️️️ Send register chunk (e.x: 10):</b>', buttons = [[Button.inline('🔙 back', data='back')]])
        raise StopPropagation
    
    elif method == 'app_mode':
        User.update(step = 'select_app_mode').where(User.user_id == event.sender_id).execute()
        await event.edit(
            '<b>🛡 Select one of the following app modes:</b>',
            buttons = [
                [Button.inline('Tg PlayStore', data='tg_google'), Button.inline('Tg Web', data='tg_standalone'), Button.inline('Tg Beta', data='tg_beta')],
                [Button.inline('🔙 back', data='back')]
            ]
        )
        raise StopPropagation
    
    elif method == 'app_version':
        User.update(step = 'select_app_version').where(User.user_id == event.sender_id).execute()
        await event.edit(
            '<b>🛡 Select one of the following app versions:</b>',
            buttons = [
                [Button.inline('• Custom version', data='custom_version')],
                [Button.inline('11.12.0 (5997)', data='version_11.12.0(5997)')],
                [Button.inline('11.11.1 (5930)', data='version_11.11.1(5930)'), Button.inline('11.11.0 (5929)', data='version_11.11.0(5929)')],
                [Button.inline('11.9.2 (5901)', data='version_11.9.2(5901)'), Button.inline('11.9.1 (5899)', data='version_11.9.1(5899)')],
                [Button.inline('11.9.0 (58379)', data='version_11.9.0(58379)'), Button.inline('11.8.3 (5799)', data='version_11.8.3(5799)')],
                [Button.inline('11.8.2 (5784)', data='version_11.8.2(5784)'), Button.inline('11.8.1 (5775)', data='version_11.8.1(5775)')],
                [Button.inline('11.8.0 (5771)', data='version_11.8.0(5771)'), Button.inline('11.7.4 (5698)', data='version_11.7.4(5698)')],
                [Button.inline('11.7.3 (5696)', data='version_11.7.3(5696)'), Button.inline('11.7.2 (5695)', data='version_11.7.2(5695)')],
                [Button.inline('11.7.0 (5663)', data='version_11.7.0(5663)')],
                [Button.inline('🔙 back', data='back')]
            ]
        )
        raise StopPropagation
    
    elif method in toggle_settings:
        setting_name = toggle_settings[method]
        Setting.update(**{setting_name: not getattr(Setting.get(), setting_name)}).execute()
        settings = Setting.get()
        
        buttons = [
            [Button.inline(f'Change reg-chunk', data='change_reg_chunk')],
            [Button.inline(f'{check_status(Setting.get().use_local_pushtoken)} Use local token', data='change_use_local_pushtoken')],
            [Button.inline('----------------------------------------------', data='none')],
            [Button.inline(f'Change AS ApiKey', data='change_antisafety_api_key')],
            [Button.inline(f'Change app mode', data='change_app_mode'), Button.inline(f'Change app version', data='change_app_version')],
            [Button.inline('----------------------------------------------', data='none')],
            [Button.inline(f'🔑 Set 2FA password' if Setting.get().set_password else '', data='change_password')],
            [Button.inline(f'📋 Update channels list' if Setting.get().join_channel else '', data='change_channels')],
            [Button.inline(f'{check_status(Setting.get().join_channel)} Join channel', data='change_join_channel'), Button.inline(f'{check_status(Setting.get().set_lastname)} Last name', data='change_set_lastname')],
            [Button.inline(f'{check_status(Setting.get().set_username)} Username', data='change_set_username'), Button.inline(f'{check_status(Setting.get().set_picture)} Picture', data='change_set_picture')],
            [Button.inline(f'{check_status(Setting.get().set_bio)} Bio', data='change_set_bio'), Button.inline(f'{check_status(Setting.get().set_password)} Password', data='change_set_password')],
            [Button.inline(f'{check_status(Setting.get().proxy)} Proxy', data='change_proxy')],
            [Button.inline('📍 Upload new proxies' if Setting.get().proxy else '', data='upload_proxies')],
            [Button.inline('----------------------------------------------', data='none')],
            [Button.inline(f'📥 Extract push-tokens ({PushToken.select().count()})' if user.id == int(os.getenv('OWNER')) else '', data='extract_device_tokens')],
            [Button.inline(f'📥 Extract emails ({Email.select().count()})' if user.id == int(os.getenv('OWNER')) else '', data='extract_emails')],
            [Button.inline(f'🤖 Turn {convert_bot_status(Setting.get().bot_status)} bot' if user.id == int(os.getenv('OWNER')) else '', data='change_bot_status')]
        ]
        
        message = '<b>⚙️ Registration settings now is:\n\n'
        
        message += f'• Reg Layer: <code>{REG_LAYER}</code>\n'
        message += f'• Bot Layer: <code>{BOT_LAYER}</code>\n\n'
        
        message += f'• AS ApiKey: <code>{Setting.get().antisafety_api_key if Setting.get().antisafety_api_key != "" else "❌"}</code>\n'
        message += f'• AS Balance: <code>${(await get_antisafety_balance(api_key=Setting.get().antisafety_api_key)).get("balance", 0) / 1_000_000 if Setting.get().antisafety_api_key != "" else "❌"}</code>\n\n'
        
        message += f'• Proxy: <code>{"✅" if Setting.get().proxy else "❌"}</code>\n'
        message += f'• Available proxies: <code>{await get_count_proxy()}</code>\n\n'
        
        message += f'• Join channel: <code>{"✅" if settings.join_channel else "❌"}</code>\n'
        message += f'• Channels: {settings.channels if settings.channels != "" else "❌"}\n\n'
        
        message += f'• Set last name: <code>{"✅" if settings.set_lastname else "❌"}</code>\n'
        message += f'• Set username: <code>{"✅" if settings.set_username else "❌"}</code>\n'
        message += f'• Set picture: <code>{"✅" if settings.set_picture else "❌"}</code>\n'
        message += f'• Set bio: <code>{"✅" if settings.set_bio else "❌"}</code>\n\n'
        
        message += f'• Use local token: <code>{"✅" if Setting.get().use_local_pushtoken else "❌"}</code>\n\n'
        
        message += f'• App version: <code>{settings.app_version}</code>\n'
        message += f'• App mode: <code>{convert_tg_application(settings.app_mode)}</code>\n'
        message += f'• 2FA password: <code>{"(" + (Setting.get().password or "Not set") + ") ✅" if Setting.get().set_password else "❌"}</code></b>\n'
        
        await event.edit(message, buttons=buttons)
    
    raise StopPropagation


@client.on(events.CallbackQuery(pattern=r'^tg_(.*)$', func=lambda e: e.is_private and User.get(User.user_id == e.sender_id).step == 'select_app_mode'))
async def tg_app_mode(event):
    user = await event.get_sender()
    user_data = User.get_or_create(user_id=user.id)
    
    method = event.pattern_match.group(1).decode('utf-8')
    
    User.update(step = 'none').where(User.user_id == event.sender_id).execute()
    Setting.update(app_mode = method).execute()
    
    buttons = [
        [Button.inline(f'Change reg-chunk', data='change_reg_chunk')],
        [Button.inline(f'{check_status(Setting.get().use_local_pushtoken)} Use local token', data='change_use_local_pushtoken')],
        [Button.inline('----------------------------------------------', data='none')],
        [Button.inline(f'Change AS ApiKey', data='change_antisafety_api_key')],
        [Button.inline(f'Change app version', data='change_app_version')],
        [Button.inline('----------------------------------------------', data='none')],
        [Button.inline(f'🔑 Set 2FA password' if Setting.get().set_password else '', data='change_password')],
        [Button.inline(f'📋 Update channels list' if Setting.get().join_channel else '', data='change_channels')],
        [Button.inline(f'{check_status(Setting.get().join_channel)} Join channel', data='change_join_channel'), Button.inline(f'{check_status(Setting.get().set_lastname)} Last name', data='change_set_lastname')],
        [Button.inline(f'{check_status(Setting.get().set_username)} Username', data='change_set_username'), Button.inline(f'{check_status(Setting.get().set_picture)} Picture', data='change_set_picture')],
        [Button.inline(f'{check_status(Setting.get().set_bio)} Bio', data='change_set_bio'), Button.inline(f'{check_status(Setting.get().set_password)} Password', data='change_set_password')],
        [Button.inline(f'{check_status(Setting.get().proxy)} Proxy', data='change_proxy')],
        [Button.inline('📍 Upload new proxies' if Setting.get().proxy else '', data='upload_proxies')],
        [Button.inline('----------------------------------------------', data='none')],
        [Button.inline(f'📥 Extract push-tokens ({PushToken.select().count()})' if user.id == int(os.getenv('OWNER')) else '', data='extract_device_tokens')],
        [Button.inline(f'📥 Extract emails ({Email.select().count()})', data='extract_emails')]
    ]
    
    await event.edit(
        '<b>⚙️ Registration settings now is:\n\n'
        
        f'• Reg Chunk: <code>{Setting.get().register_chunk}</code>\n'
        f'• Reg Layer: <code>{REG_LAYER}</code>\n'
        f'• Bot Layer: <code>{BOT_LAYER}</code>\n\n'
        
        f'• AS ApiKey: <code>{Setting.get().antisafety_api_key if Setting.get().antisafety_api_key != "" else "❌"}</code>\n'
        f'• AS Balance: <code>${(await get_antisafety_balance(api_key=Setting.get().antisafety_api_key)).get("balance", 0) / 1_000_000 if Setting.get().antisafety_api_key != "" else "❌"}</code>\n\n'
        
        f'• Proxy: <code>{"✅" if Setting.get().proxy else "❌"}</code>\n'
        f'• Available proxies: <code>{await get_count_proxy()}</code>\n\n'
        
        f'• Join channel: <code>{"✅" if Setting.get().join_channel else "❌"}</code>\n'
        f'• Channels: {Setting.get().channels if Setting.get().channels != "" else "❌"}\n\n'
        
        f'• Set last name: <code>{"✅" if Setting.get().set_lastname else "❌"}</code>\n'
        f'• Set username: <code>{"✅" if Setting.get().set_username else "❌"}</code>\n'
        f'• Set picture: <code>{"✅" if Setting.get().set_picture else "❌"}</code>\n'
        f'• Set bio: <code>{"✅" if Setting.get().set_bio else "❌"}</code>\n\n'
        
        f'• Use local token: <code>{"✅" if Setting.get().use_local_pushtoken else "❌"}</code>\n\n'
        
        f'• App version: <code>{Setting.get().app_version}</code>\n'
        f'• App mode: <code>{convert_tg_application(Setting.get().app_mode)}</code>\n'
        f'• 2FA password: <code>{"(" + (Setting.get().password or "Not set") + ") ✅" if Setting.get().set_password else "❌"}</code></b>\n',
        buttons = buttons
    )
    
    raise StopPropagation


@client.on(events.CallbackQuery(pattern=r'^version_(.*)$', func=lambda e: e.is_private and User.get(User.user_id == e.sender_id).step == 'select_app_version'))
async def version_app(event):
    user = await event.get_sender()
    user_data = User.get_or_create(user_id=user.id)
    
    method = event.pattern_match.group(1).decode('utf-8')
    
    User.update(step = 'none').where(User.user_id == event.sender_id).execute()
    Setting.update(app_version = method).execute()
    
    buttons = [
        [Button.inline(f'Change reg-chunk', data='change_reg_chunk')],
        [Button.inline(f'{check_status(Setting.get().use_local_pushtoken)} Use local token', data='change_use_local_pushtoken')],
        [Button.inline('----------------------------------------------', data='none')],
        [Button.inline(f'Change AS ApiKey', data='change_antisafety_api_key')],
        [Button.inline(f'Change app mode', data='change_app_mode'), Button.inline(f'Change app version', data='change_app_version')],
        [Button.inline('----------------------------------------------', data='none')],
        [Button.inline(f'🔑 Set 2FA password' if Setting.get().set_password else '', data='change_password')],
        [Button.inline(f'📋 Update channels list' if Setting.get().join_channel else '', data='change_channels')],
        [Button.inline(f'{check_status(Setting.get().join_channel)} Join channel', data='change_join_channel'), Button.inline(f'{check_status(Setting.get().set_lastname)} Last name', data='change_set_lastname')],
        [Button.inline(f'{check_status(Setting.get().set_username)} Username', data='change_set_username'), Button.inline(f'{check_status(Setting.get().set_picture)} Picture', data='change_set_picture')],
        [Button.inline(f'{check_status(Setting.get().set_bio)} Bio', data='change_set_bio'), Button.inline(f'{check_status(Setting.get().set_password)} Password', data='change_set_password')],
        [Button.inline(f'{check_status(Setting.get().proxy)} Proxy', data='change_proxy')],
        [Button.inline('📍 Upload new proxies' if Setting.get().proxy else '', data='upload_proxies')],
        [Button.inline('----------------------------------------------', data='none')],
        [Button.inline(f'📥 Extract push-tokens ({PushToken.select().count()})' if user.id == int(os.getenv('OWNER')) else '', data='extract_device_tokens')],
        [Button.inline(f'📥 Extract emails ({Email.select().count()})', data='extract_emails')]
    ]
    
    await event.edit(
        '<b>⚙️ Registration settings now is:\n\n'
        
        f'• Reg Chunk: <code>{Setting.get().register_chunk}</code>\n'
        f'• Reg Layer: <code>{REG_LAYER}</code>\n'
        f'• Bot Layer: <code>{BOT_LAYER}</code>\n\n'
        
        f'• AS ApiKey: <code>{Setting.get().antisafety_api_key if Setting.get().antisafety_api_key != "" else "❌"}</code>\n'
        f'• AS Balance: <code>${(await get_antisafety_balance(api_key=Setting.get().antisafety_api_key)).get("balance", 0) / 1_000_000 if Setting.get().antisafety_api_key != "" else "❌"}</code>\n\n'
        
        f'• Proxy: <code>{"✅" if Setting.get().proxy else "❌"}</code>\n'
        f'• Available proxies: <code>{await get_count_proxy()}</code>\n\n'    
        
        f'• Join channel: <code>{"✅" if Setting.get().join_channel else "❌"}</code>\n'
        f'• Channels: {Setting.get().channels if Setting.get().channels != "" else "❌"}\n\n'
        
        f'• Set last name: <code>{"✅" if Setting.get().set_lastname else "❌"}</code>\n'
        f'• Set username: <code>{"✅" if Setting.get().set_username else "❌"}</code>\n'
        f'• Set picture: <code>{"✅" if Setting.get().set_picture else "❌"}</code>\n'
        f'• Set bio: <code>{"✅" if Setting.get().set_bio else "❌"}</code>\n\n'
        
        f'• Use local token: <code>{"✅" if Setting.get().use_local_pushtoken else "❌"}</code>\n\n'
        
        f'• App version: <code>{Setting.get().app_version}</code>\n'
        f'• App mode: <code>{convert_tg_application(Setting.get().app_mode)}</code>\n'
        f'• 2FA password: <code>{"(" + (Setting.get().password or "Not set") + ") ✅" if Setting.get().set_password else "❌"}</code></b>\n',
        buttons = buttons
    )
    
    raise StopPropagation


@client.on(events.CallbackQuery(pattern=r'^custom_version$', func=lambda e: e.is_private and User.get(User.user_id == e.sender_id).step == 'select_app_version'))
async def custom_version(event):
    user = await event.get_sender()
    user_data = User.get_or_create(user_id=user.id)
    
    User.update(step = 'send_custom_version').where(User.user_id == event.sender_id).execute()
    await event.edit('<b>• Send your custom version: </b>', buttons = [[Button.inline('🔙 back', data='back')]])
    
    raise StopPropagation


@client.on(events.NewMessage(func=lambda e: e.is_private and User.get(User.user_id == e.sender_id).step == 'send_custom_version'))
async def send_custom_version(event):
    user = await event.get_sender()
    user_data = User.get_or_create(user_id=user.id)
    
    User.update(step = 'none').where(User.user_id == event.sender_id).execute()
    Setting.update(app_version = event.raw_text.strip()).execute()
    
    buttons = [
        [Button.inline(f'Change reg-chunk', data='change_reg_chunk')],
        [Button.inline(f'{check_status(Setting.get().use_local_pushtoken)} Use local token', data='change_use_local_pushtoken')],
        [Button.inline('----------------------------------------------', data='none')],
        [Button.inline(f'Change AS ApiKey', data='change_antisafety_api_key')],
        [Button.inline(f'Change app mode', data='change_app_mode'), Button.inline(f'Change app version', data='change_app_version')],
        [Button.inline('----------------------------------------------', data='none')],
        [Button.inline(f'🔑 Set 2FA password' if Setting.get().set_password else '', data='change_password')],
        [Button.inline(f'📋 Update channels list' if Setting.get().join_channel else '', data='change_channels')],
        [Button.inline(f'{check_status(Setting.get().join_channel)} Join channel', data='change_join_channel'), Button.inline(f'{check_status(Setting.get().set_lastname)} Last name', data='change_set_lastname')],
        [Button.inline(f'{check_status(Setting.get().set_username)} Username', data='change_set_username'), Button.inline(f'{check_status(Setting.get().set_picture)} Picture', data='change_set_picture')],
        [Button.inline(f'{check_status(Setting.get().set_bio)} Bio', data='change_set_bio'), Button.inline(f'{check_status(Setting.get().set_password)} Password', data='change_set_password')],
        [Button.inline(f'{check_status(Setting.get().proxy)} Proxy', data='change_proxy')],
        [Button.inline('📍 Upload new proxies' if Setting.get().proxy else '', data='upload_proxies')],
        [Button.inline('----------------------------------------------', data='none')],
        [Button.inline(f'📥 Extract push-tokens ({PushToken.select().count()})' if user.id == int(os.getenv('OWNER')) else '', data='extract_device_tokens')],
        [Button.inline(f'📥 Extract emails ({Email.select().count()})', data='extract_emails')]
    ]
    
    await event.reply(
        '<b>⚙️ Registration settings now is:\n\n'
        
        f'• Reg Chunk: <code>{Setting.get().register_chunk}</code>\n'
        f'• Reg Layer: <code>{REG_LAYER}</code>\n'
        f'• Bot Layer: <code>{BOT_LAYER}</code>\n\n'
        
        f'• AS ApiKey: <code>{Setting.get().antisafety_api_key if Setting.get().antisafety_api_key != "" else "❌"}</code>\n'
        f'• AS Balance: <code>${(await get_antisafety_balance(api_key=Setting.get().antisafety_api_key)).get("balance", 0) / 1_000_000 if Setting.get().antisafety_api_key != "" else "❌"}</code>\n\n'
        
        f'• Proxy: <code>{"✅" if Setting.get().proxy else "❌"}</code>\n'
        f'• Available proxies: <code>{await get_count_proxy()}</code>\n\n'    
        
        f'• Join channel: <code>{"✅" if Setting.get().join_channel else "❌"}</code>\n'
        f'• Channels: {Setting.get().channels if Setting.get().channels != "" else "❌"}\n\n'
        
        f'• Set last name: <code>{"✅" if Setting.get().set_lastname else "❌"}</code>\n'
        f'• Set username: <code>{"✅" if Setting.get().set_username else "❌"}</code>\n'
        f'• Set picture: <code>{"✅" if Setting.get().set_picture else "❌"}</code>\n'
        f'• Set bio: <code>{"✅" if Setting.get().set_bio else "❌"}</code>\n\n'
        
        f'• Use local token: <code>{"✅" if Setting.get().use_local_pushtoken else "❌"}</code>\n\n'
        
        f'• App version: <code>{Setting.get().app_version}</code>\n'
        f'• App mode: <code>{convert_tg_application(Setting.get().app_mode)}</code>\n'
        f'• 2FA password: <code>{"(" + (Setting.get().password or "Not set") + ") ✅" if Setting.get().set_password else "❌"}</code></b>\n',
        buttons = buttons
    )
    
    raise StopPropagation


@client.on(events.NewMessage(func=lambda e: e.is_private and User.get(User.user_id == e.sender_id).step == 'send_2fa_password'))
async def set_2fa_password(event):
    user = await event.get_sender()
    user_data = User.get_or_create(user_id=user.id)
    
    User.update(step = 'none').where(User.user_id == event.sender_id).execute()
    Setting.update(password = event.raw_text.strip()).execute()
    
    buttons = [
        [Button.inline(f'Change reg-chunk', data='change_reg_chunk')],
        [Button.inline(f'{check_status(Setting.get().use_local_pushtoken)} Use local token', data='change_use_local_pushtoken')],
        [Button.inline('----------------------------------------------', data='none')],
        [Button.inline(f'Change AS ApiKey', data='change_antisafety_api_key')],
        [Button.inline(f'Change app mode', data='change_app_mode'), Button.inline(f'Change app version', data='change_app_version')],
        [Button.inline('----------------------------------------------', data='none')],
        [Button.inline(f'🔑 Set 2FA password' if Setting.get().set_password else '', data='change_password')],
        [Button.inline(f'📋 Update channels list' if Setting.get().join_channel else '', data='change_channels')],
        [Button.inline(f'{check_status(Setting.get().join_channel)} Join channel', data='change_join_channel'), Button.inline(f'{check_status(Setting.get().set_lastname)} Last name', data='change_set_lastname')],
        [Button.inline(f'{check_status(Setting.get().set_username)} Username', data='change_set_username'), Button.inline(f'{check_status(Setting.get().set_picture)} Picture', data='change_set_picture')],
        [Button.inline(f'{check_status(Setting.get().set_bio)} Bio', data='change_set_bio'), Button.inline(f'{check_status(Setting.get().set_password)} Password', data='change_set_password')],
        [Button.inline(f'{check_status(Setting.get().proxy)} Proxy', data='change_proxy')],
        [Button.inline('📍 Upload new proxies' if Setting.get().proxy else '', data='upload_proxies')],
        [Button.inline('----------------------------------------------', data='none')],
        [Button.inline(f'📥 Extract push-tokens ({PushToken.select().count()})' if user.id == int(os.getenv('OWNER')) else '', data='extract_device_tokens')],
        [Button.inline(f'📥 Extract emails ({Email.select().count()})', data='extract_emails')]
    ]
    
    await event.reply(
        '<b>⚙️ Registration settings now is:\n\n'
        
        f'• Reg Chunk: <code>{Setting.get().register_chunk}</code>\n'
        f'• Reg Layer: <code>{REG_LAYER}</code>\n'
        f'• Bot Layer: <code>{BOT_LAYER}</code>\n\n'
        
        f'• AS ApiKey: <code>{Setting.get().antisafety_api_key if Setting.get().antisafety_api_key != "" else "❌"}</code>\n'
        f'• AS Balance: <code>${(await get_antisafety_balance(api_key=Setting.get().antisafety_api_key)).get("balance", 0) / 1_000_000 if Setting.get().antisafety_api_key != "" else "❌"}</code>\n\n'
        
        f'• Proxy: <code>{"✅" if Setting.get().proxy else "❌"}</code>\n'
        f'• Available proxies: <code>{await get_count_proxy()}</code>\n\n'
        
        f'• Join channel: <code>{"✅" if Setting.get().join_channel else "❌"}</code>\n'
        f'• Channels: {Setting.get().channels if Setting.get().channels != "" else "❌"}\n\n'
        
        f'• Set last name: <code>{"✅" if Setting.get().set_lastname else "❌"}</code>\n'
        f'• Set username: <code>{"✅" if Setting.get().set_username else "❌"}</code>\n'
        f'• Set picture: <code>{"✅" if Setting.get().set_picture else "❌"}</code>\n'
        f'• Set bio: <code>{"✅" if Setting.get().set_bio else "❌"}</code>\n\n'
        
        f'• Use local token: <code>{"✅" if Setting.get().use_local_pushtoken else "❌"}</code>\n\n'
        
        f'• App version: <code>{Setting.get().app_version}</code>\n'
        f'• App mode: <code>{convert_tg_application(Setting.get().app_mode)}</code>\n'
        f'• 2FA password: <code>{"(" + (Setting.get().password or "Not set") + ") ✅" if Setting.get().set_password else "❌"}</code></b>\n',
        buttons = buttons
    )
    
    raise StopPropagation


@client.on(events.NewMessage(func=lambda e: e.is_private and User.get(User.user_id == e.sender_id).step == 'send_channels_list'))
async def send_channels_list(event):
    user = await event.get_sender()
    user_data = User.get_or_create(user_id=user.id)
    
    if not event.raw_text.startswith('@'):
        await event.reply('<b>⚠️ Your sent channel is invalid, you have to send channels with @</b>', buttons = [[Button.inline('🔙 back', data='back')]])
        raise StopPropagation
    
    User.update(step = 'none').where(User.user_id == event.sender_id).execute()
    Setting.update(channels = event.raw_text.strip()).execute()
    
    buttons = [
        [Button.inline(f'Change reg-chunk', data='change_reg_chunk')],
        [Button.inline(f'{check_status(Setting.get().use_local_pushtoken)} Use local token', data='change_use_local_pushtoken')],
        [Button.inline('----------------------------------------------', data='none')],
        [Button.inline(f'Change AS ApiKey', data='change_antisafety_api_key')],
        [Button.inline(f'Change app mode', data='change_app_mode'), Button.inline(f'Change app version', data='change_app_version')],
        [Button.inline('----------------------------------------------', data='none')],
        [Button.inline(f'🔑 Set 2FA password' if Setting.get().set_password else '', data='change_password')],
        [Button.inline(f'📋 Update channels list' if Setting.get().join_channel else '', data='change_channels')],
        [Button.inline(f'{check_status(Setting.get().join_channel)} Join channel', data='change_join_channel'), Button.inline(f'{check_status(Setting.get().set_lastname)} Last name', data='change_set_lastname')],
        [Button.inline(f'{check_status(Setting.get().set_username)} Username', data='change_set_username'), Button.inline(f'{check_status(Setting.get().set_picture)} Picture', data='change_set_picture')],
        [Button.inline(f'{check_status(Setting.get().set_bio)} Bio', data='change_set_bio'), Button.inline(f'{check_status(Setting.get().set_password)} Password', data='change_set_password')],
        [Button.inline(f'{check_status(Setting.get().proxy)} Proxy', data='change_proxy')],
        [Button.inline('📍 Upload new proxies' if Setting.get().proxy else '', data='upload_proxies')],
        [Button.inline('----------------------------------------------', data='none')],
        [Button.inline(f'📥 Extract push-tokens ({PushToken.select().count()})' if user.id == int(os.getenv('OWNER')) else '', data='extract_device_tokens')],
        [Button.inline(f'📥 Extract emails ({Email.select().count()})', data='extract_emails')]
    ]
    
    await event.reply(
        '<b>⚙️ Registration settings now is:\n\n'
        
        f'• Reg Chunk: <code>{Setting.get().register_chunk}</code>\n'
        f'• Reg Layer: <code>{REG_LAYER}</code>\n'
        f'• Bot Layer: <code>{BOT_LAYER}</code>\n\n'
        
        f'• AS ApiKey: <code>{Setting.get().antisafety_api_key if Setting.get().antisafety_api_key != "" else "❌"}</code>\n'
        f'• AS Balance: <code>${(await get_antisafety_balance(api_key=Setting.get().antisafety_api_key)).get("balance", 0) / 1_000_000 if Setting.get().antisafety_api_key != "" else "❌"}</code>\n\n'
        
        f'• Proxy: <code>{"✅" if Setting.get().proxy else "❌"}</code>\n'
        f'• Available proxies: <code>{await get_count_proxy()}</code>\n\n'
        
        f'• Join channel: <code>{"✅" if Setting.get().join_channel else "❌"}</code>\n'
        f'• Channels: {Setting.get().channels if Setting.get().channels != "" else "❌"}\n\n'
        
        f'• Set last name: <code>{"✅" if Setting.get().set_lastname else "❌"}</code>\n'
        f'• Set username: <code>{"✅" if Setting.get().set_username else "❌"}</code>\n'
        f'• Set picture: <code>{"✅" if Setting.get().set_picture else "❌"}</code>\n'
        f'• Set bio: <code>{"✅" if Setting.get().set_bio else "❌"}</code>\n\n'
        
        f'• Use local token: <code>{"✅" if Setting.get().use_local_pushtoken else "❌"}</code>\n\n'
        
        f'• App version: <code>{Setting.get().app_version}</code>\n'
        f'• App mode: <code>{convert_tg_application(Setting.get().app_mode)}</code>\n'
        f'• 2FA password: <code>{"(" + (Setting.get().password or "Not set") + ") ✅" if Setting.get().set_password else "❌"}</code></b>\n',
        buttons = buttons
    )
    
    raise StopPropagation


@client.on(events.NewMessage(func=lambda e: e.is_private and User.get(User.user_id == e.sender_id).step == 'send_antisafety_api_key'))
async def send_antisafety_api_key(event):
    user = await event.get_sender()
    user_data = User.get_or_create(user_id=user.id)
    
    User.update(step = 'none').where(User.user_id == event.sender_id).execute()
    Setting.update(antisafety_api_key = event.raw_text.strip()).execute()
    
    buttons = [
        [Button.inline(f'Change reg-chunk', data='change_reg_chunk')],
        [Button.inline(f'{check_status(Setting.get().use_local_pushtoken)} Use local token', data='change_use_local_pushtoken')],
        [Button.inline('----------------------------------------------', data='none')],
        [Button.inline(f'Change AS ApiKey', data='change_antisafety_api_key')],
        [Button.inline(f'Change app mode', data='change_app_mode'), Button.inline(f'Change app version', data='change_app_version')],
        [Button.inline('----------------------------------------------', data='none')],
        [Button.inline(f'🔑 Set 2FA password' if Setting.get().set_password else '', data='change_password')],
        [Button.inline(f'📋 Update channels list' if Setting.get().join_channel else '', data='change_channels')],
        [Button.inline(f'{check_status(Setting.get().join_channel)} Join channel', data='change_join_channel'), Button.inline(f'{check_status(Setting.get().set_lastname)} Last name', data='change_set_lastname')],
        [Button.inline(f'{check_status(Setting.get().set_username)} Username', data='change_set_username'), Button.inline(f'{check_status(Setting.get().set_picture)} Picture', data='change_set_picture')],
        [Button.inline(f'{check_status(Setting.get().set_bio)} Bio', data='change_set_bio'), Button.inline(f'{check_status(Setting.get().set_password)} Password', data='change_set_password')],
        [Button.inline(f'{check_status(Setting.get().proxy)} Proxy', data='change_proxy')],
        [Button.inline('📍 Upload new proxies' if Setting.get().proxy else '', data='upload_proxies')],
        [Button.inline('----------------------------------------------', data='none')],
        [Button.inline(f'📥 Extract push-tokens ({PushToken.select().count()})' if user.id == int(os.getenv('OWNER')) else '', data='extract_device_tokens')],
        [Button.inline(f'📥 Extract emails ({Email.select().count()})', data='extract_emails')]
    ]
    
    await event.reply(
        '<b>⚙️ Registration settings now is:\n\n'
        
        f'• Reg Chunk: <code>{Setting.get().register_chunk}</code>\n'
        f'• Reg Layer: <code>{REG_LAYER}</code>\n'
        f'• Bot Layer: <code>{BOT_LAYER}</code>\n\n'
        
        f'• AS ApiKey: <code>{Setting.get().antisafety_api_key if Setting.get().antisafety_api_key != "" else "❌"}</code>\n'
        f'• AS Balance: <code>${(await get_antisafety_balance(api_key=Setting.get().antisafety_api_key)).get("balance", 0) / 1_000_000 if Setting.get().antisafety_api_key != "" else "❌"}</code>\n\n'
        
        f'• Proxy: <code>{"✅" if Setting.get().proxy else "❌"}</code>\n'
        f'• Available proxies: <code>{await get_count_proxy()}</code>\n\n'
        
        f'• Join channel: <code>{"✅" if Setting.get().join_channel else "❌"}</code>\n'
        f'• Channels: {Setting.get().channels if Setting.get().channels != "" else "❌"}\n\n'
        
        f'• Set last name: <code>{"✅" if Setting.get().set_lastname else "❌"}</code>\n'
        f'• Set username: <code>{"✅" if Setting.get().set_username else "❌"}</code>\n'
        f'• Set picture: <code>{"✅" if Setting.get().set_picture else "❌"}</code>\n'
        f'• Set bio: <code>{"✅" if Setting.get().set_bio else "❌"}</code>\n\n'
        
        f'• Use local token: <code>{"✅" if Setting.get().use_local_pushtoken else "❌"}</code>\n\n'
        
        f'• App version: <code>{Setting.get().app_version}</code>\n'
        f'• App mode: <code>{convert_tg_application(Setting.get().app_mode)}</code>\n'
        f'• 2FA password: <code>{"(" + (Setting.get().password or "Not set") + ") ✅" if Setting.get().set_password else "❌"}</code></b>\n',
        buttons = buttons
    )
    
    raise StopPropagation


@client.on(events.NewMessage(func=lambda e: e.is_private and User.get(User.user_id == e.sender_id).step == 'send_reg_chunk'))
async def send_antisafety_api_key(event):
    user = await event.get_sender()
    user_data = User.get_or_create(user_id=user.id)
    
    User.update(step = 'none').where(User.user_id == event.sender_id).execute()
    Setting.update(register_chunk = event.raw_text.strip()).execute()
    
    buttons = [
        [Button.inline(f'Change reg-chunk', data='change_reg_chunk')],
        [Button.inline(f'{check_status(Setting.get().use_local_pushtoken)} Use local token', data='change_use_local_pushtoken')],
        [Button.inline('----------------------------------------------', data='none')],
        [Button.inline(f'Change AS ApiKey', data='change_antisafety_api_key')],
        [Button.inline(f'Change app mode', data='change_app_mode'), Button.inline(f'Change app version', data='change_app_version')],
        [Button.inline('----------------------------------------------', data='none')],
        [Button.inline(f'🔑 Set 2FA password' if Setting.get().set_password else '', data='change_password')],
        [Button.inline(f'📋 Update channels list' if Setting.get().join_channel else '', data='change_channels')],
        [Button.inline(f'{check_status(Setting.get().join_channel)} Join channel', data='change_join_channel'), Button.inline(f'{check_status(Setting.get().set_lastname)} Last name', data='change_set_lastname')],
        [Button.inline(f'{check_status(Setting.get().set_username)} Username', data='change_set_username'), Button.inline(f'{check_status(Setting.get().set_picture)} Picture', data='change_set_picture')],
        [Button.inline(f'{check_status(Setting.get().set_bio)} Bio', data='change_set_bio'), Button.inline(f'{check_status(Setting.get().set_password)} Password', data='change_set_password')],
        [Button.inline(f'{check_status(Setting.get().proxy)} Proxy', data='change_proxy')],
        [Button.inline('📍 Upload new proxies' if Setting.get().proxy else '', data='upload_proxies')],
        [Button.inline('----------------------------------------------', data='none')],
        [Button.inline(f'📥 Extract push-tokens ({PushToken.select().count()})' if user.id == int(os.getenv('OWNER')) else '', data='extract_device_tokens')],
        [Button.inline(f'📥 Extract emails ({Email.select().count()})', data='extract_emails')]
    ]
    
    await event.reply(
        '<b>⚙️ Registration settings now is:\n\n'
        
        f'• Reg Chunk: <code>{Setting.get().register_chunk}</code>\n'
        f'• Reg Layer: <code>{REG_LAYER}</code>\n'
        f'• Bot Layer: <code>{BOT_LAYER}</code>\n\n'
        
        f'• AS ApiKey: <code>{Setting.get().antisafety_api_key if Setting.get().antisafety_api_key != "" else "❌"}</code>\n'
        f'• AS Balance: <code>${(await get_antisafety_balance(api_key=Setting.get().antisafety_api_key)).get("balance", 0) / 1_000_000 if Setting.get().antisafety_api_key != "" else "❌"}</code>\n\n'
        
        f'• Proxy: <code>{"✅" if Setting.get().proxy else "❌"}</code>\n'
        f'• Available proxies: <code>{await get_count_proxy()}</code>\n\n'
        
        f'• Join channel: <code>{"✅" if Setting.get().join_channel else "❌"}</code>\n'
        f'• Channels: {Setting.get().channels if Setting.get().channels != "" else "❌"}\n\n'
        
        f'• Set last name: <code>{"✅" if Setting.get().set_lastname else "❌"}</code>\n'
        f'• Set username: <code>{"✅" if Setting.get().set_username else "❌"}</code>\n'
        f'• Set picture: <code>{"✅" if Setting.get().set_picture else "❌"}</code>\n'
        f'• Set bio: <code>{"✅" if Setting.get().set_bio else "❌"}</code>\n\n'
        
        f'• Use local token: <code>{"✅" if Setting.get().use_local_pushtoken else "❌"}</code>\n\n'
        
        f'• App version: <code>{Setting.get().app_version}</code>\n'
        f'• App mode: <code>{convert_tg_application(Setting.get().app_mode)}</code>\n'
        f'• 2FA password: <code>{"(" + (Setting.get().password or "Not set") + ") ✅" if Setting.get().set_password else "❌"}</code></b>\n',
        buttons = buttons
    )
    
    raise StopPropagation

@client.on(events.CallbackQuery(pattern=r'^extract_device_tokens$', func=lambda e: e.is_private, chats=int(os.getenv('OWNER'))))
async def extract_push_tokens(event):
    user = await event.get_sender()
    user_data = User.get_or_create(user_id=user.id)
    
    await event.answer('<b>⏱️ Please wait few second ...</b>', alert=False)
    
    push_tokens = PushToken.select()
    csv_data = 'api_key,token\n'
    for token in push_tokens:
        csv_data += f'{token.api_key},{token.token}\n'
    
    with open(f'push_tokens_{event.sender_id}.csv', 'w', encoding='utf-8') as f:
        f.write(csv_data)
    
    await client.send_file(
        entity  = event.chat_id,
        file    = f'push_tokens_{event.sender_id}.csv',
        caption = f'<b>📮 Push tokens extracted successfully.\n📥 Total tokens: {push_tokens.count()}</b>'
    )
    
    os.unlink(f'push_tokens_{event.sender_id}.csv')
    
    raise StopPropagation


@client.on(events.CallbackQuery(pattern=r'^extract_emails$', func=lambda e: e.is_private, chats=int(os.getenv('OWNER'))))
async def extract_emails(event):
    user = await event.get_sender()
    user_data = User.get_or_create(user_id=user.id)
    
    await event.answer('<b>⏱️ Please wait few second ...</b>', alert=False)
    
    emails = Email.select()
    csv_data = 'api_key,email,uuid\n'
    for email in emails:
        csv_data += f'{email.api_key},{email.email},{email.uuid}\n'
    
    with open(f'emails_{event.sender_id}.csv', 'w', encoding='utf-8') as f:
        f.write(csv_data)
    
    await client.send_file(
        entity  = event.chat_id,
        file    = f'emails_{event.sender_id}.csv',
        caption = f'<b>📧 Emails extracted successfully.\n📥 Total emails: {emails.count()}</b>'
    )
    
    os.unlink(f'emails_{event.sender_id}.csv')
    
    raise StopPropagation

# --------------------------------------------------- #

@client.on(events.NewMessage(pattern=r'^/antisafety_balance$', func=lambda e: e.is_private))
async def antisafety_balance(event):
    user = await event.get_sender()
    user_data = User.get_or_create(user_id=user.id)
    
    wait = await event.reply('<b>⏱️ Please wait few second ...</b>')
    
    antisafety = await get_antisafety_balance(api_key=os.getenv('ANTISAFETY_API_KEY'))
    if antisafety.get('status', False):
        balance, currency = antisafety.get('balance', 0) / 1_000_000, antisafety.get('currency', 'USD')
        await wait.edit(f'<b>💸 Balance: <code>{balance:.6f}</code>\n💶 Currency: <code>{currency}</code></b>')
    else:
        await wait.edit(f'<b>⚠️ Try again after few second ...</b>')
    
    raise StopPropagation


@client.on(events.NewMessage(pattern=r'^/clear$', func=lambda e: e.is_private))
async def clear_handler(event):
    user = await event.get_sender()
    user_data = User.get_or_create(user_id=user.id)
    
    wait = await event.reply('<b>⏱️ Please wait few second ...</b>')
    
    folders = [
        f'registeration/temp_sessions/{event.sender_id}',
        f'registeration/valid_sessions/{event.sender_id}',
        f'registeration/otp_sessions/{event.sender_id}'
    ]
    
    for folder in folders:
        if os.path.exists(folder):
            for item in os.listdir(folder):
                os.remove(os.path.join(folder, item))
    
    await wait.edit('<b>✅ All sessions on folder cleared successfully!</b>')
    
    raise StopPropagation


@client.on(events.NewMessage(pattern=r'^/clear_all$', func=lambda e: e.is_private))
async def clear_all(event):
    wait = await event.reply('<b>⏱️ Please wait few second ...</b>')
    
    folders = [
        'registeration/temp_sessions',
        'registeration/valid_sessions',
        'registeration/otp_sessions'
    ]
    
    for folder in folders:
        if os.path.exists(folder):
            for item in os.listdir(folder):
                if os.path.isdir(os.path.join(folder, item)):
                    shutil.rmtree(os.path.join(folder, item))
                else:
                    os.remove(os.path.join(folder, item))
    
    await wait.edit('<b>✅ All sessions on folder cleared successfully!</b>')
    
    raise StopPropagation


@client.on(events.NewMessage(pattern=r'^/count$', func=lambda e: e.is_private))
async def count_handler(event):
    await event.reply(f'<b>✅ Total accounts registered: <code>{len(os.listdir(f"registeration/valid_sessions/{event.sender_id}")) / 2}</code></b>')
    
    raise StopPropagation


@client.on(events.NewMessage(pattern=r'^/extract$', func=lambda e: e.is_private))
async def extract_all_valid_sessions(event):
    wait = await event.reply('<b>⏱️ Please wait few second ...</b>')
    
    zip_path = 'registeration/accounts.zip'
    shutil.make_archive('registeration/accounts', 'zip', f'registeration/valid_sessions/{event.sender_id}')
    num_accounts = len(os.listdir(f'registeration/valid_sessions/{event.sender_id}')) / 2
    
    sent = await client.send_file(
        entity = event.chat_id,
        file = zip_path,
        caption = f'<b>📁 Total accounts: {num_accounts}</b>',
    )
    
    os.unlink(zip_path)
    
    await wait.delete()
    await sent.reply('<b>✅ All valid sessions extracted successfully!</b>')

    raise StopPropagation

# --------------------------------------------------- #

@client.on(events.NewMessage(func=lambda e: e.file and (e.file.name.endswith('.session') or e.file.name.endswith('.json'))))
async def otp_handler(event):
    if event.file.name.endswith('.session'):
        wait = await event.reply('<b>⏱️ Please wait few second ...</b>')
        
    file_name = event.file.name
    file = await event.download_media()
    
    os.makedirs(f'registeration/otp_sessions/{event.sender_id}', exist_ok=True)
    if not os.path.exists(f'registeration/otp_sessions/{event.sender_id}/{file_name}'):
        os.rename(file, f'registeration/otp_sessions/{event.sender_id}/{file_name}')
    
    if file_name.endswith('.session'):
        buttons = [
            [Button.inline('📥 Get OTP', data=f'otp_{Path(file).stem}')],
            [Button.inline('🔴 Disable 2FA', data=f'disable_2fa_{Path(file).stem}')]
        ]
        await wait.edit(
            f'<b>✅ Session file received!\n\n👇 Click on the bellow button for receive OTP code:\n\n📞 Phone number: <code>{Path(file).stem}</code></b>',
            buttons = buttons
        )
    
    raise StopPropagation


@client.on(events.CallbackQuery(pattern=r'^otp_(.*)$', func=lambda e: e.is_private))
async def otp_handler(event):
    await event.edit('<b>⏱️ Please wait few second ...</b>')
    
    phone_number = event.pattern_match.group(1).decode('utf-8')
    file = f'registeration/otp_sessions/{event.sender_id}/{phone_number}.session'
    
    if not os.path.exists(file):
        return await event.edit(f'<b>❌ Session file not found!\n\n📞 Phone: <code>{phone_number}</code></b>')
    
    otp = await get_otp(phone_number, event.sender_id)
    if not otp[0]:
        return await event.edit(f'<b>❌ Failed to get OTP code!\n\n📞 Phone: <code>{phone_number}</code>\n⚠️ Reason: {otp[1]}</b>')
    
    buttons = [
        [Button.inline('🔄 Re-get code', data=f'otp_{phone_number}'), Button.inline('🚪 Logout', data=f'logout_{phone_number}')]
    ]
    await event.edit(f'<b>✅ The login-code (OTP) received successfully from the account.\n\n👉 Phone: <code>{phone_number}</code>\n👉 OTP: <code>{otp[1]}</code></b>', buttons = buttons)


@client.on(events.CallbackQuery(pattern=r'^disable_2fa_(.*)$', func=lambda e: e.is_private))
async def disable_2fa_handler(event):
    await event.edit('<b>⏱️ Please wait few second ...</b>')
    
    phone_number = event.pattern_match.group(1).decode('utf-8')
    response = await disable_2fa(phone_number, event.sender_id)
    
    if response:
        buttons = [[Button.inline('📥 Get OTP', data=f'otp_{phone_number}')], [Button.inline('✅ 2FA Disabled Successfully', data=f'none')]]
    else:
        buttons = [[Button.inline('📥 Get OTP', data=f'otp_{phone_number}')], [Button.inline('🔴 Disable 2FA', data=f'disable_2fa_{phone_number}')]]
    
    await event.edit(
        f'<b>✅ Session file received!\n\n👇 Click on the bellow button for receive OTP code:\n\n📞 Phone number: <code>{phone_number}</code></b>',
        buttons = buttons
    )


@client.on(events.CallbackQuery(pattern=r'^logout_(.*)$', func=lambda e: e.is_private))
async def logout_handler(event):
    await event.edit('<b>⏱️ Please wait few second ...</b>')
    
    phone_number = event.pattern_match.group(1).decode('utf-8')
    response = await logout_from_session(phone_number, event.sender_id)
    
    if not response:
        return await event.edit(f'<b>❌ Failed to logout!\n\n📞 Phone: <code>{phone_number}</code>\n⚠️ Reason: {response[1]}</b>')
    
    await event.edit(f'<b>✅ Logged out successfully!\n\n👉 Phone: <code>{phone_number}</code></b>')
    
    raise StopPropagation

# --------------------------------------------------- #

@client.on(events.NewMessage(func=lambda e: e.file and e.file.name.endswith('.txt')))
async def file_handler(event):
    user = await event.get_sender()
    user_data = User.get_or_create(user_id=user.id)

    wait = await event.reply('<b>⏱️ Please wait few second ...</b>')

    antisafety = await get_antisafety_balance(api_key=Setting.get().antisafety_api_key)
    if antisafety.get('status', False):
        balance = antisafety.get('balance', 0)
        if balance <= 0.015:
            return await event.reply(f'<b>⚠️ You have to charge your antisafety account!\n💸 Balance: <code>{balance}</code></b>')

    if event.message.message == '' and not event.message.message:
        return await wait.edit(f'<b>⚠️ Caption required.</b>')

    caption = event.message.message or '----'
    file = await event.download_media()
    with open(file, 'r') as f:
        numbers = f.read().split('\n')
        numbers = [n.strip() for n in numbers if n.strip()]
    os.remove(file)

    register_chunk = Setting.get().register_chunk if hasattr(Setting.get(), 'register_chunk') and Setting.get().register_chunk > 0 else 5
    total_numbers = len(numbers)
    success, unsuccess = 0, 0

    await wait.edit(f'<b>⏱️ Please wait ([s: 0 | u: 0] / {total_numbers})</b>')

    global is_cancelled
    is_cancelled = False

    import asyncio

    sem = asyncio.Semaphore(register_chunk)

    results = [None] * total_numbers  # (success, message)

    async def register_entry(idx, number_obj):
        nonlocal success, unsuccess, caption
        try:
            async with sem:
                if is_cancelled:
                    return ('cancelled', None)

                split = number_obj.split(caption)
                if len(split) < 2:
                    number, link = (split[0] if split[0].startswith('+') else '+' + split[0], '')
                else:
                    number, link = split[0] if split[0].startswith('+') else '+' + split[0], split[1]

                msg_idx = idx + 1
                processing = await event.reply(f'<b>⚙️ Registraing <code>{number}</code> ...</b>')

                if not is_valid_phone_number(number):
                    unsuccess += 1
                    await processing.edit(f'<b>❌ [{msg_idx}] Invalid phone number!\n\n📞 Phone: <code>{number}</code></b>')
                    return (False, f'<b>❌ [{msg_idx}] Invalid phone number!\n\n📞 Phone: <code>{number}</code></b>')

                # proxy = await get_proxy()
                if Setting.get().proxy:
                    proxy = await get_proxy()
                    if proxy:
                        ip_location = await get_proxy_location(proxy)
                        if ip_location:
                            #await processing.edit(f'<b>⏱️ Please wait few second ...\n\n🔑 Proxy: <code>{ip_location["ip"]}</code>\n🌏 Location: <code>{ip_location["city"]}, {ip_location["region"]}, {ip_location["country"]}</code></b>')
                            await asyncio.sleep(0.5)
                        else:
                            unsuccess += 1
                            await processing.edit(f'<b>⚠️ [{msg_idx}] Couldn\'t connect to proxy <code>{proxy["addr"]}:{proxy["port"]}</code>\n\n -> /cancel  /cancel</b>')
                            return (False, f'<b>⚠️ [{msg_idx}] Couldn\'t connect to proxy <code>{proxy["addr"]}:{proxy["port"]}</code>\n\n -> /cancel  /cancel</b>')
                else:
                    proxy = None
                #await processing.edit(f'<b>🔑 Setting up proxy <code>{number}</code> ...</b>')

                register = Register(phone_number=number, user_id=event.sender_id, proxy=proxy)
                #await processing.edit(f'<b>🪛 Patching the client <code>{number}</code> ...</b>')
                await register._patch_client()

                #await processing.edit(f'<b>💬 Sending sms verifation code <code>{number}</code> ...</b>')
                response = await register.get_sms_code()

                if type(response) == tuple and response[0] == False:
                    unsuccess += 1
                    await processing.edit(f'<b>❌ [{msg_idx}] Failed to get sms code!\n\n📞 Phone: <code>{number}</code>\n⚠️ Reason: {response[1]}</b>')
                    return (False, f'<b>❌ [{msg_idx}] Failed to get sms code!\n\n📞 Phone: <code>{number}</code>\n⚠️ Reason: {response[1]}</b>')

                elif await register.alreay_exists(response):
                    unsuccess += 1
                    await processing.edit(f'<b>❌ [{msg_idx}] Failed to get sms code!\n\n💬 Type: <code>{response.type}</code>\n\n📞 Phone: <code>{number}</code>\n⚠️ Reason: account already exists</b>')
                    return (False, f'<b>❌ [{msg_idx}] Failed to get sms code!\n\n💬 Type: <code>{response.type}</code>\n\n📞 Phone: <code>{number}</code>\n⚠️ Reason: account already exists</b>')

                elif await register.need_setup_email(response.type):
                    #await processing.edit(f'<b>📧 Setting up email verification from website <code>{number}</code> ...</b>')
                    setup_email_response, setup_email_msg = await register.setup_email()

                    if not setup_email_response:
                        unsuccess += 1
                        await processing.edit(f'<b>❌ [{msg_idx}] Failed to setup email verification!\n\n📞 Phone: <code>{number}</code>\n⚠️ Reason: {setup_email_msg}</b>')
                        return (False, f'<b>❌ [{msg_idx}] Failed to setup email verification!\n\n📞 Phone: <code>{number}</code>\n⚠️ Reason: {setup_email_msg}</b>')

                elif await register.need_resend_code(response):
                    #await processing.edit(f'<b>🔄 Re-send code via SMS to <code>{number}</code> ...</b>')
                    await asyncio.sleep(5.0)
                    response = await register.resend_code_request()

                await asyncio.sleep(0.5)
                #await processing.edit(f'<b>📞 Phone: <code>{number}</code>\n💬 Type: <code>{getattr(response, "type", "")}</code>\n\n🔄 Receiving sms verifation code ...</b>')

                code, attempts = None, 0
                while not code and attempts < 5:
                    code = await extract_code(await send_request(link, timeout=10.0))
                    if not code:
                        await asyncio.sleep(1.5)
                        attempts += 1

                if not code:
                    unsuccess += 1
                    await processing.edit(f'<b>❌[{msg_idx}] Failed to get code after 5 attempts!\n\n📞 Phone: <code>{number}</code></b>')
                    return (False, f'<b>❌[{msg_idx}] Failed to get code after 5 attempts!\n\n📞 Phone: <code>{number}</code></b>')

                first_name = get_random_name()
                last_name = get_random_last_name()

                #await processing.edit(f'<b>🔰 Signing up in tg <code>{number}</code> ...</b>')
                signup, signup_msg = await register.sign_up(int(code), first_name=first_name, last_name=last_name)

                if signup:
                    success += 1
                    await processing.delete()
                    await event.reply(
                        f'<b>✅ [{msg_idx}] Your account successfully registered!\n\n👤 Full name: {first_name} {last_name}\n📞 Phone: <code>{number}</code></b>',
                        buttons=[[Button.url(f'{number}', url=f'https://t.me/{number}')]]
                    )
                    return (True, None)
                else:
                    unsuccess += 1
                    await processing.edit(f'<b>❌[{msg_idx}] Failed to register!\n\n📞 Phone: <code>{number}</code>\n⚠️ Reason: {signup_msg}</b>')
                    return (False, f'<b>❌[{msg_idx}] Failed to register!\n\n📞 Phone: <code>{number}</code>\n⚠️ Reason: {signup_msg}</b>')

        except Exception as e:
            unsuccess += 1
            return (False, f'<b>❌ Exception: {e}</b>')
        finally:
            try:
                await wait.edit(f'<b>⏱️ Please wait ([s: {success} | u: {unsuccess}] / {total_numbers})</b>')
            except:
                pass

    tasks = []

    for idx, item in enumerate(numbers):
        if is_cancelled:
            break
        tasks.append(register_entry(idx, item))

    # Run chunked for fairness - batches of register_chunk
    results = []
    for i in range(0, len(tasks), register_chunk):
        if is_cancelled:
            break
        chunk = tasks[i:i + register_chunk]
        chunk_results = await asyncio.gather(*chunk)
        results.extend(chunk_results)
        if is_cancelled:
            break

    await wait.edit(
        f'<b>✅ Proccess finished successfully.\n\n</b>• <b>Total accounts: {len(numbers)}</b>\n• <b>Success: {success}</b>\n• <b>Unsuccess: {unsuccess}</b>'
    )

    zip_path = 'registeration/accounts.zip'
    shutil.make_archive('registeration/accounts', 'zip', f'registeration/valid_sessions/{event.sender_id}')
    num_accounts = len([x for x in os.listdir(f'registeration/valid_sessions/{event.sender_id}') if x.endswith('.session') or x.endswith('.json')]) // 2

    await client.send_file(
        entity=event.chat_id,
        file=zip_path,
        caption=f'<b>✅ All accounts registered successfully.\n📁 Total accounts: {num_accounts}</b>'
    )

    os.unlink(zip_path)

    # Clean up session and json files
    for session_file in os.listdir(f'registeration/valid_sessions/{event.sender_id}'):
        sf_path = f'registeration/valid_sessions/{event.sender_id}/{session_file}'
        if session_file.endswith('.session') or session_file.endswith('.json'):
            os.unlink(sf_path)


@client.on(events.NewMessage(func=lambda e: not e.file))
async def register_handler(event):
    user = await event.get_sender()
    user_data = User.get_or_create(user_id=user.id)
    
    async with client.conversation(event.chat_id, timeout=1200) as conv:
        text = event.raw_text.strip()
        
        #wait = await event.reply('<b>⏱️ Please wait few second ...</b>')
        setting = Setting.get()
        
        antisafety = await get_antisafety_balance(api_key=setting.antisafety_api_key)
        if antisafety.get('status', False):
            balance = antisafety.get('balance', 0)
            if balance <= 0.015:
                return await wait.edit(f'<b>⚠️ You have to charge your antisafety account!\n💸 Balance: <code>{balance}</code></b>')
        
        await wait.delete()
        
        if not text.startswith('+') and not is_valid_phone_number(text):
            await conv.send_message('<b>❌ Invalid phone number, please send your phone number again:</b>')
            return
            
        #wait = await conv.send_message('<b>⏱️ Please wait few second, connecting to proxy and server ...</b>')
        
        if setting.proxy:
            proxy = await get_proxy()
            if proxy:
                ip_location = await get_proxy_location(proxy)
                if ip_location:
                    #await wait.edit(f'<b>⏱️ Please wait few second ...\n\n🔑 Proxy: <code>{ip_location["ip"]}</code>\n🌏 Location: <code>{ip_location["city"]}, {ip_location["region"]}, {ip_location["country"]}</code></b>')
                    await asyncio.sleep(1.2)
                else:
                    return await wait.edit(f'<b>⚠️ Couldn\'t connect to proxy <code>{proxy["addr"]}:{proxy["port"]}</code>\n\n -> /cancel  /cancel</b>')
        else:
            proxy = None

        register = Register(phone_number=text, user_id=event.sender_id, proxy=proxy)
        start_time = time.time()
        
        #await wait.edit(f'<b>🪛 Patching the tg client ...</b>')
        await register._patch_client()
        
        #await wait.edit(f'<b>💬 Sending sms verifation code ...</b>')
        sms_sent = await register.get_sms_code()
        
        print(sms_sent)
        
        if type(sms_sent) == tuple and sms_sent[0] == False:
            return await wait.edit(f'<b>❌ Failed to get sms code!\n\n📞 Phone: <code>{text}</code>\n⚠️ Reason: {sms_sent[1]}</b>')

        elif await register.alreay_exists(sms_sent):
            return await wait.edit(f'<b>❌ Failed to get sms code!\n\n💬 Type: <code>{sms_sent.type}</code>\n\n📞 Phone: <code>{text}</code>\n⚠️ Reason: account already exists</b>')
        
        elif await register.need_setup_email(sms_sent.type):
            if setting.email_setup_type == 'auto':
                #await wait.edit('<b>📧 Setting up email verification from website ...</b>')
                setup = await register.setup_email()
                print(setup)
                if not setup:
                    return await wait.edit(f'<b>❌ Failed to get email code!\n\n📞 Phone: <code>{text}</code>\n⚠️ Reason: bad email</b>')
            
            elif setting.email_setup_type == 'manual':
                #await wait.edit('<b>📧 Enter your email address (e.g: <code>example@gmail.com</code>) :</b>')
                email = await conv.get_response()

                if email.text in ['/cancel', '/start']:
                    return
                
                #wait = await conv.send_message('<b>⏱️ Please wait few second ...</b>')
                
                email_code_sent = await register.setup_manual_email(email.raw_text)
                
                if email_code_sent:
                    #await wait.edit(f'<b>✅ Code sent to your email: <code>{email.raw_text}</code>\n\n🔢 Enter your email code (6 digits):</b>')
                    
                    while True:
                        code = await conv.get_response()
                        
                        if code.text == '/cancel':
                            return
                        
                        if not code.text.isdigit() or len(code.text) != 6:
                            #await conv.send_message('<b>❌ Your sent email code is invalid!\n\n🔢 Enter your email code (6 digits):</b>')
                            continue
                        
                        #wait = await conv.send_message('<b>⏱️ Please wait few second ...</b>')
                        await register.setup_manual_email_code(code.raw_text)

                        break
        
        elif await register.need_resend_code(sms_sent):
            #await wait.edit('<b>🔄 Re-send code via SMS, please wait 5 seconds ...</b>')
            await asyncio.sleep(5.0)
            sms_sent = await register.resend_code_request()

        elif any(text.startswith(prefix) for prefix in ['+33', '+31', '+971', '+972', '+48', '+506', '+351', '+380']) and await register.need_call_request(sms_sent):
            await wait.edit(f'<b>💤 Client sleep 120s for resend code via call ...\n\n📞 Phone: <code>{text}</code></b>')
            await asyncio.sleep(125.0)
            sms_sent = await register.resend_code_request()

        await wait.edit(f'<b>⏱️ Time taken: ~<code>{time.time() - start_time:.2f}s</code>\n\n✅ Code sent to: <code>{text}</code>\n💬 Type: <code>{sms_sent.type}</code>\n\n🔢 Enter your sms code (5 digits):</b>')
        while True:
            code = await conv.get_response()
            
            if code.text in ['/cancel', '/start']:
                return
                
            if code.text.isdigit() and len(code.text) == 5:
                break
                
            await conv.send_message('<b>❌ Your sent sms code is invalid!\n\n🔢 Enter your sms code (5 digits):</b>')
            
        #wait = await conv.send_message('<b>⏱️ Please wait few second ...</b>')
        
        await wait.edit('<b>👤 Enter first name for account:\n\n👉 For set default: /default</b>')
        
        first_name = (await conv.get_response()).text.strip()
        
        #wait = await conv.send_message('<b>⏱️ Please wait few second ...</b>')
        
        if first_name in ['/cancel', '/start']:
            return
        
        if first_name == '/default':
            first_name = get_random_name()
        last_name = get_random_last_name()
        
        #await wait.edit(f'<b>🔰 Signing up in tg ...</b>')
        signup, signup_msg = await register.sign_up(int(code.text), first_name=first_name, last_name=last_name)

        if signup:
            await wait.delete()
            
            if os.path.exists(f'registeration/valid_sessions/{event.sender_id}/{text}.json'):
                await client.send_file(
                    entity = event.chat_id,
                    file = f'registeration/valid_sessions/{event.sender_id}/{text}.json',
                    caption = f'⚙️ <b>{text}.json</b>'
                )
                
            await client.send_file(
                entity  = event.chat_id,
                file    = f'registeration/valid_sessions/{event.sender_id}/{text}.session',
                caption = f'<b>✅ Your account Successfully registered!\n\n👤 Full name: {first_name} {last_name}\n📞 Phone: <code>{text}</code></b>',
                buttons = [[Button.url(f'{text}', url=f'https://t.me/{text}')]]
            )
            
            os.unlink(f'registeration/valid_sessions/{event.sender_id}/{text}.session')
        
        else:
            await wait.edit(f'<b>❌ Failed to register!\n\n⚠️ Reason: {signup_msg}\n\n👉 /cancel</b>')

# --------------------------------------------------- #

print('[+] Registration bot started ...')
client.run_until_disconnected()
