DNS Changer

 #!/usr/bin/env python3

"""

DNS Auto Changer - Complete Package

Combines the main application, build tool, and installer in one file.


Usage:

  python dns_auto_changer_complete.py           # Run the DNS changer application

  python dns_auto_changer_complete.py --build   # Build executable

  python dns_auto_changer_complete.py --install # Install Task Scheduler (for .exe)

"""


import sys

import os


# Check command line arguments to determine mode

if __name__ == "__main__":

    if "--build" in sys.argv:

        # Build mode - compile to executable

        MODE = "BUILD"

    elif "--install" in sys.argv or "--install-auto" in sys.argv:

        # Install mode - setup Task Scheduler

        MODE = "INSTALL"

    else:

        # Run mode - normal application

        MODE = "RUN"

else:

    MODE = "RUN"


# ============================================================================

# BUILD MODE - Executable Compiler

# ============================================================================

if MODE == "BUILD":

    import subprocess

    import shutil

    from pathlib import Path


    def find_icon_file():

        """Find an image file in the current directory to use as icon"""

        image_extensions = ['.png', '.jpg', '.jpeg', '.bmp', '.gif', '.ico']

        current_dir = Path('.')

        

        for ext in image_extensions:

            for file in current_dir.glob(f'*{ext}'):

                if file.is_file():

                    print(f"Found image file: {file}")

                    return str(file)

        return None


    def convert_image_to_ico(image_path):

        """Convert image to .ico format using PIL"""

        try:

            from PIL import Image

            

            if image_path.lower().endswith('.ico'):

                return image_path

            

            img = Image.open(image_path)

            icon_sizes = [(256, 256), (128, 128), (64, 64), (48, 48), (32, 32), (16, 16)]

            ico_path = "temp_icon.ico"

            img.save(ico_path, format='ICO', sizes=icon_sizes)

            

            print(f"✓ Converted {image_path} to {ico_path}")

            return ico_path

        except ImportError:

            print("Warning: PIL/Pillow not available for icon conversion")

            print("Install with: pip install pillow")

            return None

        except Exception as e:

            print(f"Warning: Could not convert image to icon: {e}")

            return None


    def check_pyinstaller():

        """Check if PyInstaller is installed"""

        try:

            import PyInstaller

            return True

        except ImportError:

            return False


    def install_pyinstaller():

        """Install PyInstaller"""

        print("PyInstaller is not installed.")

        print("Installing PyInstaller...")

        try:

            subprocess.check_call([sys.executable, "-m", "pip", "install", "pyinstaller"])

            print("✓ PyInstaller installed successfully!")

            return True

        except Exception as e:

            print(f"✗ Failed to install PyInstaller: {e}")

            return False


    def build_exe():

        """Build the executable using PyInstaller"""

        try:

            print("\n" + "=" * 70)

            print("Building DNS Auto Changer executable...")

            print("=" * 70)

            

            script_name = os.path.basename(__file__)

            if not os.path.exists(script_name):

                print(f"✗ ERROR: {script_name} not found in current directory!")

                return False

            

            print("\nSearching for icon file...")

            icon_file = find_icon_file()

            

            if icon_file:

                print(f"✓ Using icon: {icon_file}")

                if not icon_file.lower().endswith('.ico'):

                    print("Converting image to .ico format...")

                    ico_file = convert_image_to_ico(icon_file)

                    if ico_file:

                        icon_file = ico_file

                    else:

                        print("Warning: Icon conversion failed, building without icon")

                        icon_file = None

            else:

                print("No icon file found in directory")

                print("Supported formats: .png, .jpg, .jpeg, .bmp, .gif, .ico")

                print("Building without icon...")

            

            cmd = [

                "pyinstaller",

                "--onefile",

                "--windowed",

                "--name=DNS_Auto_Changer",

                "--clean",

            ]

            

            if icon_file:

                cmd.append(f"--icon={icon_file}")

            

            cmd.append(script_name)

            

            print(f"\nRunning: {' '.join(cmd)}\n")

            print("-" * 70)

            

            result = subprocess.run(cmd, capture_output=True, text=True)

            

            if icon_file and icon_file == "temp_icon.ico" and os.path.exists("temp_icon.ico"):

                try:

                    os.remove("temp_icon.ico")

                    print("\n✓ Cleaned up temporary icon file")

                except:

                    pass

            

            if result.returncode == 0:

                print("-" * 70)

                print("\n✓ Build completed successfully!")

                

                exe_path = os.path.join("dist", "DNS_Auto_Changer.exe")

                if os.path.exists(exe_path):

                    file_size = os.path.getsize(exe_path) / (1024 * 1024)

                    print(f"\n✓ Executable created: {exe_path}")

                    print(f"  Size: {file_size:.2f} MB")

                    

                    if icon_file:

                        print(f"  Icon: Embedded successfully")

                    

                    cleanup_build_files()

                    

                    # Auto-install Task Scheduler

                    print("\n" + "=" * 70)

                    print("AUTO-INSTALLING TASK SCHEDULER...")

                    print("=" * 70)

                    auto_install_task_scheduler(exe_path)

                    

                    return True

                else:

                    print("\n✗ Executable was not created!")

                    return False

            else:

                print("-" * 70)

                print("\n✗ Build failed!")

                print(f"\nError output:\n{result.stderr}")

                return False

                

        except Exception as e:

            print(f"\n✗ Build error: {e}")

            import traceback

            traceback.print_exc()

            return False


    def auto_install_task_scheduler(exe_path):

        """Automatically install Task Scheduler without user prompts"""

        try:

            import ctypes

            

            # Check if running as admin

            try:

                is_admin = ctypes.windll.shell32.IsUserAnAdmin()

            except:

                is_admin = False

            

            if not is_admin:

                print("\n⚠ Not running as administrator - requesting elevation for Task Scheduler setup...")

                # Request elevation and run install with the exe path as parameter

                try:

                    # Pass the exe path as an environment variable since we can't pass complex args

                    import tempfile

                    temp_path_file = os.path.join(tempfile.gettempdir(), 'dns_changer_exe_path.txt')

                    with open(temp_path_file, 'w') as f:

                        f.write(os.path.abspath(exe_path))

                    

                    ret = ctypes.windll.shell32.ShellExecuteW(

                        None,

                        "runas",

                        sys.executable,

                        f'"{os.path.abspath(__file__)}" --install-auto',

                        None,

                        1

                    )

                    if ret > 32:

                        print("✓ Task Scheduler installation launched with admin privileges")

                        # Wait a bit for the elevated process to complete

                        time.sleep(2)

                        

                        # Check if installation was successful

                        if os.path.exists(os.path.join(os.path.dirname(os.path.abspath(exe_path)), "Launch_DNS_Changer.bat")):

                            print("✓ Task Scheduler installation completed successfully")

                            return True

                        else:

                            print("⚠ Task Scheduler installation may still be in progress")

                            return True

                    else:

                        print("✗ Failed to elevate privileges for Task Scheduler installation")

                        return False

                except Exception as e:

                    print(f"✗ Could not request elevation: {e}")

                    return False

            

            # If already admin, install directly

            abs_exe_path = os.path.abspath(exe_path)

            task_name = "DNSAutoChanger"

            

            task_xml = f'''<?xml version="1.0" encoding="UTF-16"?>

<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">

  <RegistrationInfo>

    <Description>DNS Auto Changer - Runs with administrator privileges without UAC prompts</Description>

    <Author>{os.getenv('USERNAME')}</Author>

  </RegistrationInfo>

  <Triggers />

  <Principals>

    <Principal id="Author">

      <UserId>{os.getenv('USERDOMAIN')}\\{os.getenv('USERNAME')}</UserId>

      <LogonType>InteractiveToken</LogonType>

      <RunLevel>HighestAvailable</RunLevel>

    </Principal>

  </Principals>

  <Settings>

    <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>

    <DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>

    <StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>

    <AllowHardTerminate>true</AllowHardTerminate>

    <StartWhenAvailable>false</StartWhenAvailable>

    <RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>

    <IdleSettings>

      <StopOnIdleEnd>true</StopOnIdleEnd>

      <RestartOnIdle>false</RestartOnIdle>

    </IdleSettings>

    <AllowStartOnDemand>true</AllowStartOnDemand>

    <Enabled>true</Enabled>

    <Hidden>false</Hidden>

    <RunOnlyIfIdle>false</RunOnlyIfIdle>

    <WakeToRun>false</WakeToRun>

    <ExecutionTimeLimit>PT0S</ExecutionTimeLimit>

    <Priority>7</Priority>

  </Settings>

  <Actions Context="Author">

    <Exec>

      <Command>"{abs_exe_path}"</Command>

      <WorkingDirectory>{os.path.dirname(abs_exe_path)}</WorkingDirectory>

    </Exec>

  </Actions>

</Task>'''

            

            temp_xml_path = os.path.join(os.environ['TEMP'], 'dns_changer_task.xml')

            with open(temp_xml_path, 'w', encoding='utf-16') as f:

                f.write(task_xml)

            

            print(f"\nCreating scheduled task: {task_name}")

            print(f"Executable path: {abs_exe_path}")

            

            # Delete existing task if it exists

            subprocess.run(

                ['schtasks', '/Delete', '/TN', task_name, '/F'],

                capture_output=True,

                shell=True

            )

            

            # Create new scheduled task

            result = subprocess.run(

                ['schtasks', '/Create', '/TN', task_name, '/XML', temp_xml_path],

                capture_output=True,

                text=True,

                shell=True

            )

            

            try:

                os.remove(temp_xml_path)

            except:

                pass

            

            if result.returncode == 0:

                print(f"✓ Scheduled task '{task_name}' created successfully!")

                

                # Create smart launcher bat file that auto-updates Task Scheduler path

                launcher_path = os.path.join(os.path.dirname(abs_exe_path), "Launch_DNS_Changer.bat")

                launcher_content = f'''@echo off

setlocal EnableDelayedExpansion


REM Get the directory where this bat file is located

set "SCRIPT_DIR=%~dp0"

set "SCRIPT_DIR=%SCRIPT_DIR:~0,-1%"


REM Expected exe path

set "EXE_PATH=%SCRIPT_DIR%\\DNS_Auto_Changer.exe"


REM Check if exe exists

if not exist "!EXE_PATH!" (

    echo ERROR: DNS_Auto_Changer.exe not found in current directory!

    echo Please make sure the exe is in the same folder as this bat file.

    pause

    exit /b 1

)


REM Check if Task Scheduler task exists

schtasks /Query /TN "{task_name}" >nul 2>&1

if errorlevel 1 (

    echo Task Scheduler not configured. Setting up now...

    call :UpdateTaskScheduler

    goto :Launch

)


REM Get current task path

for /f "tokens=*" %%i in ('schtasks /Query /TN "{task_name}" /FO LIST /V ^| findstr /C:"Task To Run"') do set "TASK_LINE=%%i"

set "TASK_PATH=!TASK_LINE:*Task To Run:=!"

REM Remove leading/trailing spaces and quotes

set "TASK_PATH=!TASK_PATH:~1!"

set "TASK_PATH=!TASK_PATH: =!"

set "TASK_PATH=!TASK_PATH:"=!"

set "EXE_PATH_CLEAN=!EXE_PATH: =!"

set "EXE_PATH_CLEAN=!EXE_PATH_CLEAN:"=!"


REM Compare paths

if not "!TASK_PATH!"=="!EXE_PATH_CLEAN!" (

    echo Detected location change. Updating Task Scheduler...

    call :UpdateTaskScheduler

)


:Launch

echo Starting DNS Auto Changer...

schtasks /Run /TN "{task_name}" >nul 2>&1

echo DNS Auto Changer has been launched!

timeout /t 2 >nul

exit /b 0


:UpdateTaskScheduler

echo Updating Task Scheduler with current location...


REM Create temporary XML

set "TEMP_XML=%TEMP%\\dns_task_update.xml"


(

echo ^<?xml version="1.0" encoding="UTF-16"?^>

echo ^<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task"^>

echo   ^<RegistrationInfo^>

echo     ^<Description^>DNS Auto Changer - Runs with administrator privileges without UAC prompts^</Description^>

echo     ^<Author^>%USERNAME%^</Author^>

echo   ^</RegistrationInfo^>

echo   ^<Triggers /^>

echo   ^<Principals^>

echo     ^<Principal id="Author"^>

echo       ^<UserId^>%USERDOMAIN%\\%USERNAME%^</UserId^>

echo       ^<LogonType^>InteractiveToken^</LogonType^>

echo       ^<RunLevel^>HighestAvailable^</RunLevel^>

echo     ^</Principal^>

echo   ^</Principals^>

echo   ^<Settings^>

echo     ^<MultipleInstancesPolicy^>IgnoreNew^</MultipleInstancesPolicy^>

echo     ^<DisallowStartIfOnBatteries^>false^</DisallowStartIfOnBatteries^>

echo     ^<StopIfGoingOnBatteries^>false^</StopIfGoingOnBatteries^>

echo     ^<AllowHardTerminate^>true^</AllowHardTerminate^>

echo     ^<StartWhenAvailable^>false^</StartWhenAvailable^>

echo     ^<RunOnlyIfNetworkAvailable^>false^</RunOnlyIfNetworkAvailable^>

echo     ^<AllowStartOnDemand^>true^</AllowStartOnDemand^>

echo     ^<Enabled^>true^</Enabled^>

echo     ^<Hidden^>false^</Hidden^>

echo     ^<RunOnlyIfIdle^>false^</RunOnlyIfIdle^>

echo     ^<WakeToRun^>false^</WakeToRun^>

echo     ^<ExecutionTimeLimit^>PT0S^</ExecutionTimeLimit^>

echo     ^<Priority^>7^</Priority^>

echo   ^</Settings^>

echo   ^<Actions Context="Author"^>

echo     ^<Exec^>

echo       ^<Command^>!EXE_PATH!^</Command^>

echo       ^<WorkingDirectory^>!SCRIPT_DIR!^</WorkingDirectory^>

echo     ^</Exec^>

echo   ^</Actions^>

echo ^</Task^>

) > "!TEMP_XML!"


REM Delete old task and create new one

schtasks /Delete /TN "{task_name}" /F >nul 2>&1

schtasks /Create /TN "{task_name}" /XML "!TEMP_XML!" /F >nul 2>&1


if errorlevel 1 (

    echo ERROR: Failed to update Task Scheduler.

    echo This requires administrator privileges.

    echo Please run this batch file as administrator once.

    del "!TEMP_XML!" >nul 2>&1

    pause

    exit /b 1

)


del "!TEMP_XML!" >nul 2>&1

echo Task Scheduler updated successfully!

exit /b 0

'''

                with open(launcher_path, 'w') as f:

                    f.write(launcher_content)

                

                print(f"✓ Smart launcher created: {launcher_path}")

                print(f"  → Auto-updates Task Scheduler when moved to new location!")

                

                # Auto-create desktop shortcut

                try:

                    desktop_path = os.path.join(os.path.expanduser('~'), 'Desktop')

                    shortcut_path = os.path.join(desktop_path, 'DNS Auto Changer.lnk')

                    

                    vbs_script = f'''Set oWS = WScript.CreateObject("WScript.Shell")

Set oLink = oWS.CreateShortcut("{shortcut_path}")

oLink.TargetPath = "{launcher_path}"

oLink.WorkingDirectory = "{os.path.dirname(launcher_path)}"

oLink.Description = "DNS Auto Changer - No UAC"

oLink.Save

'''

                    vbs_path = os.path.join(os.environ['TEMP'], 'create_shortcut.vbs')

                    with open(vbs_path, 'w') as f:

                        f.write(vbs_script)

                    

                    subprocess.run(['cscript', '//nologo', vbs_path], shell=True, capture_output=True)

                    os.remove(vbs_path)

                    

                    print(f"✓ Desktop shortcut created: {shortcut_path}")

                except Exception as e:

                    print(f"⚠ Could not create desktop shortcut: {e}")

                

                return True

            else:

                print(f"✗ Failed to create scheduled task")

                print(f"Error: {result.stderr}")

                return False

                

        except Exception as e:

            print(f"✗ Error during auto-install: {e}")

            import traceback

            traceback.print_exc()

            return False


    def cleanup_build_files():

        """Clean up build artifacts"""

        try:

            print("\nCleaning up build files...")

            

            if os.path.exists("build"):

                shutil.rmtree("build")

                print("✓ Removed build directory")

            

            if os.path.exists("DNS_Auto_Changer.spec"):

                os.remove("DNS_Auto_Changer.spec")

                print("✓ Removed spec file")

        except Exception as e:

            print(f"Warning: Could not clean up some files: {e}")


    def build_main():

        print("\n" + "=" * 70)

        print("DNS AUTO CHANGER - BUILD TOOL")

        print("=" * 70)

        print("\nThis script will compile into a standalone executable")

        print("(DNS_Auto_Changer.exe)")

        print("\n💡 TIP: Place an image file (png, jpg, ico, etc.) in this")

        print("   directory to use it as the executable icon!")

        print("\n🚀 Auto-install: Task Scheduler will be configured automatically")

        print("=" * 70)

        

        if not check_pyinstaller():

            print("\nPyInstaller not found. Installing automatically...")

            if not install_pyinstaller():

                print("\n✗ Build cancelled. Could not install PyInstaller.")

                input("\nPress Enter to exit...")

                return

        

        print("\n" + "=" * 70)

        print("STARTING BUILD PROCESS...")

        print("=" * 70)

        

        if build_exe():

            print("\n" + "=" * 70)

            print("✓ BUILD AND INSTALLATION SUCCESSFUL!")

            print("=" * 70)

            print("\n📁 Files created:")

            print("   • dist\\DNS_Auto_Changer.exe")

            print("   • dist\\Launch_DNS_Changer.bat")

            print("   • Desktop shortcut (if successful)")

            print("\n🎯 How to use:")

            print("   1. Double-click 'Launch_DNS_Changer.bat' (NO UAC!)")

            print("   2. Or use the desktop shortcut")

            print("   3. Or run DNS_Auto_Changer.exe as administrator")

            print("\n✅ Task Scheduler configured - no UAC prompts when using launcher!")

            print("=" * 70)

        else:

            print("\n" + "=" * 70)

            print("✗ BUILD FAILED")

            print("=" * 70)

            print("\nPlease check the error messages above.")

        

        input("\nPress Enter to exit...")


    # Execute build mode

    try:

        build_main()

    except KeyboardInterrupt:

        print("\n\nBuild cancelled by user.")

    except Exception as e:

        print(f"\n\nUnexpected error: {e}")

        import traceback

        traceback.print_exc()

    

    sys.exit(0)


# ============================================================================

# INSTALL MODE - Task Scheduler Setup

# ============================================================================

elif MODE == "INSTALL":

    import subprocess

    import ctypes

    import platform


    def is_admin():

        """Check if running with administrator privileges"""

        try:

            return ctypes.windll.shell32.IsUserAnAdmin()

        except:

            return False


    def run_as_admin():

        """Restart installer with administrator privileges"""

        try:

            if platform.system() == "Windows":

                if getattr(sys, 'frozen', False):

                    application_path = sys.executable

                    params = ' '.join(sys.argv[1:])

                else:

                    application_path = sys.executable

                    script = os.path.abspath(__file__)

                    params = f'"{script}" --install'

                

                ret = ctypes.windll.shell32.ShellExecuteW(

                    None, 

                    "runas",

                    application_path,

                    params,

                    None, 

                    1

                )

                

                if ret > 32:

                    sys.exit(0)

                else:

                    return False

            return True

        except Exception as e:

            print(f"Failed to elevate privileges: {e}")

            return False


    def create_task_scheduler():

        """Create a scheduled task that runs DNS changer .exe with admin privileges"""

        try:

            # Check if this is auto-install mode (called from build)

            import tempfile

            temp_path_file = os.path.join(tempfile.gettempdir(), 'dns_changer_exe_path.txt')

            

            if "--install-auto" in sys.argv and os.path.exists(temp_path_file):

                # Read exe path from temp file

                with open(temp_path_file, 'r') as f:

                    dns_changer_path = f.read().strip()

                try:

                    os.remove(temp_path_file)

                except:

                    pass

            else:

                # Manual install mode - look for exe in current directory

                dns_changer_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "DNS_Auto_Changer.exe")

            

            if not os.path.exists(dns_changer_path):

                print(f"ERROR: DNS_Auto_Changer.exe not found at: {dns_changer_path}")

                print("Please make sure DNS_Auto_Changer.exe is in the same directory.")

                print(f"Run 'python {os.path.basename(__file__)} --build' to create it first.")

                return False

            

            task_name = "DNSAutoChanger"

            

            task_xml = f'''<?xml version="1.0" encoding="UTF-16"?>

<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">

  <RegistrationInfo>

    <Description>DNS Auto Changer - Runs with administrator privileges without UAC prompts</Description>

    <Author>{os.getenv('USERNAME')}</Author>

  </RegistrationInfo>

  <Triggers />

  <Principals>

    <Principal id="Author">

      <UserId>{os.getenv('USERDOMAIN')}\\{os.getenv('USERNAME')}</UserId>

      <LogonType>InteractiveToken</LogonType>

      <RunLevel>HighestAvailable</RunLevel>

    </Principal>

  </Principals>

  <Settings>

    <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>

    <DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>

    <StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>

    <AllowHardTerminate>true</AllowHardTerminate>

    <StartWhenAvailable>false</StartWhenAvailable>

    <RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>

    <IdleSettings>

      <StopOnIdleEnd>true</StopOnIdleEnd>

      <RestartOnIdle>false</RestartOnIdle>

    </IdleSettings>

    <AllowStartOnDemand>true</AllowStartOnDemand>

    <Enabled>true</Enabled>

    <Hidden>false</Hidden>

    <RunOnlyIfIdle>false</RunOnlyIfIdle>

    <WakeToRun>false</WakeToRun>

    <ExecutionTimeLimit>PT0S</ExecutionTimeLimit>

    <Priority>7</Priority>

  </Settings>

  <Actions Context="Author">

    <Exec>

      <Command>"{dns_changer_path}"</Command>

      <WorkingDirectory>{os.path.dirname(dns_changer_path)}</WorkingDirectory>

    </Exec>

  </Actions>

</Task>'''

            

            temp_xml_path = os.path.join(os.environ['TEMP'], 'dns_changer_task.xml')

            with open(temp_xml_path, 'w', encoding='utf-16') as f:

                f.write(task_xml)

            

            print("=" * 70)

            print("DNS Auto Changer - Installation")

            print("=" * 70)

            print(f"\nExecutable: {dns_changer_path}")

            print(f"\nCreating scheduled task: {task_name}")

            

            subprocess.run(

                ['schtasks', '/Delete', '/TN', task_name, '/F'],

                capture_output=True,

                shell=True

            )

            

            result = subprocess.run(

                ['schtasks', '/Create', '/TN', task_name, '/XML', temp_xml_path],

                capture_output=True,

                text=True,

                shell=True

            )

            

            try:

                os.remove(temp_xml_path)

            except:

                pass

            

            if result.returncode == 0:

                print(f"✓ Scheduled task '{task_name}' created successfully!")

                print("\n" + "=" * 70)

                print("Installation completed successfully!")

                print("=" * 70)

                

                create_launcher(task_name)

                return True

            else:

                print(f"✗ Failed to create scheduled task.")

                print(f"Error: {result.stderr}")

                return False

                

        except Exception as e:

            print(f"ERROR: {e}")

            import traceback

            traceback.print_exc()

            return False


    def create_launcher(task_name):

        """Create a smart launcher that auto-updates Task Scheduler path"""

        try:

            launcher_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "Launch_DNS_Changer.bat")

            

            launcher_content = f'''@echo off

setlocal EnableDelayedExpansion


REM Get the directory where this bat file is located

set "SCRIPT_DIR=%~dp0"

set "SCRIPT_DIR=%SCRIPT_DIR:~0,-1%"


REM Expected exe path

set "EXE_PATH=%SCRIPT_DIR%\\DNS_Auto_Changer.exe"


REM Check if exe exists

if not exist "!EXE_PATH!" (

    echo ERROR: DNS_Auto_Changer.exe not found in current directory!

    echo Please make sure the exe is in the same folder as this bat file.

    pause

    exit /b 1

)


REM Check if Task Scheduler task exists

schtasks /Query /TN "{task_name}" >nul 2>&1

if errorlevel 1 (

    echo Task Scheduler not configured. Setting up now...

    call :UpdateTaskScheduler

    goto :Launch

)


REM Get current task path

for /f "tokens=*" %%i in ('schtasks /Query /TN "{task_name}" /FO LIST /V ^| findstr /C:"Task To Run"') do set "TASK_LINE=%%i"

set "TASK_PATH=!TASK_LINE:*Task To Run:=!"

REM Remove leading/trailing spaces and quotes

set "TASK_PATH=!TASK_PATH:~1!"

set "TASK_PATH=!TASK_PATH: =!"

set "TASK_PATH=!TASK_PATH:"=!"

set "EXE_PATH_CLEAN=!EXE_PATH: =!"

set "EXE_PATH_CLEAN=!EXE_PATH_CLEAN:"=!"


REM Compare paths

if not "!TASK_PATH!"=="!EXE_PATH_CLEAN!" (

    echo Detected location change. Updating Task Scheduler...

    call :UpdateTaskScheduler

)


:Launch

echo Starting DNS Auto Changer...

schtasks /Run /TN "{task_name}" >nul 2>&1

echo DNS Auto Changer has been launched!

timeout /t 2 >nul

exit /b 0


:UpdateTaskScheduler

echo Updating Task Scheduler with current location...


REM Create temporary XML

set "TEMP_XML=%TEMP%\\dns_task_update.xml"


(

echo ^<?xml version="1.0" encoding="UTF-16"?^>

echo ^<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task"^>

echo   ^<RegistrationInfo^>

echo     ^<Description^>DNS Auto Changer - Runs with administrator privileges without UAC prompts^</Description^>

echo     ^<Author^>%USERNAME%^</Author^>

echo   ^</RegistrationInfo^>

echo   ^<Triggers /^>

echo   ^<Principals^>

echo     ^<Principal id="Author"^>

echo       ^<UserId^>%USERDOMAIN%\\%USERNAME%^</UserId^>

echo       ^<LogonType^>InteractiveToken^</LogonType^>

echo       ^<RunLevel^>HighestAvailable^</RunLevel^>

echo     ^</Principal^>

echo   ^</Principals^>

echo   ^<Settings^>

echo     ^<MultipleInstancesPolicy^>IgnoreNew^</MultipleInstancesPolicy^>

echo     ^<DisallowStartIfOnBatteries^>false^</DisallowStartIfOnBatteries^>

echo     ^<StopIfGoingOnBatteries^>false^</StopIfGoingOnBatteries^>

echo     ^<AllowHardTerminate^>true^</AllowHardTerminate^>

echo     ^<StartWhenAvailable^>false^</StartWhenAvailable^>

echo     ^<RunOnlyIfNetworkAvailable^>false^</RunOnlyIfNetworkAvailable^>

echo     ^<AllowStartOnDemand^>true^</AllowStartOnDemand^>

echo     ^<Enabled^>true^</Enabled^>

echo     ^<Hidden^>false^</Hidden^>

echo     ^<RunOnlyIfIdle^>false^</RunOnlyIfIdle^>

echo     ^<WakeToRun^>false^</WakeToRun^>

echo     ^<ExecutionTimeLimit^>PT0S^</ExecutionTimeLimit^>

echo     ^<Priority^>7^</Priority^>

echo   ^</Settings^>

echo   ^<Actions Context="Author"^>

echo     ^<Exec^>

echo       ^<Command^>!EXE_PATH!^</Command^>

echo       ^<WorkingDirectory^>!SCRIPT_DIR!^</WorkingDirectory^>

echo     ^</Exec^>

echo   ^</Actions^>

echo ^</Task^>

) > "!TEMP_XML!"


REM Delete old task and create new one

schtasks /Delete /TN "{task_name}" /F >nul 2>&1

schtasks /Create /TN "{task_name}" /XML "!TEMP_XML!" /F >nul 2>&1


if errorlevel 1 (

    echo ERROR: Failed to update Task Scheduler.

    echo This requires administrator privileges.

    echo Please run this batch file as administrator once.

    del "!TEMP_XML!" >nul 2>&1

    pause

    exit /b 1

)


del "!TEMP_XML!" >nul 2>&1

echo Task Scheduler updated successfully!

exit /b 0

'''

            

            with open(launcher_path, 'w') as f:

                f.write(launcher_content)

            

            print(f"\n✓ Smart launcher created: {launcher_path}")

            print("  → Auto-updates Task Scheduler when files are moved!")

            print("\nUSAGE:")

            print("-" * 70)

            print("1. Double-click 'Launch_DNS_Changer.bat' (NO UAC in same location)")

            print("2. If you move files: first run will ask for admin to update,")

            print("   then subsequent runs will work without UAC!")

            print("-" * 70)

            

            create_shortcut_option(launcher_path)

            

        except Exception as e:

            print(f"Warning: Could not create launcher: {e}")


    def create_shortcut_option(launcher_path):

        """Offer to create a desktop shortcut"""

        try:

            desktop_path = os.path.join(os.path.expanduser('~'), 'Desktop')

            shortcut_path = os.path.join(desktop_path, 'DNS Auto Changer.lnk')

            

            print("\n" + "=" * 70)

            response = input("Would you like to create a desktop shortcut? (y/n): ").strip().lower()

            

            if response == 'y':

                vbs_script = f'''Set oWS = WScript.CreateObject("WScript.Shell")

Set oLink = oWS.CreateShortcut("{shortcut_path}")

oLink.TargetPath = "{launcher_path}"

oLink.WorkingDirectory = "{os.path.dirname(launcher_path)}"

oLink.Description = "DNS Auto Changer - No UAC"

oLink.Save

'''

                vbs_path = os.path.join(os.environ['TEMP'], 'create_shortcut.vbs')

                with open(vbs_path, 'w') as f:

                    f.write(vbs_script)

                

                subprocess.run(['cscript', '//nologo', vbs_path], shell=True)

                os.remove(vbs_path)

                

                print(f"✓ Desktop shortcut created: {shortcut_path}")

        except Exception as e:

            print(f"Warning: Could not create desktop shortcut: {e}")


    def install_main():

        print("\n" + "=" * 70)

        print("DNS AUTO CHANGER - INSTALLER")

        print("=" * 70)

        print("\nThis installer will configure DNS Auto Changer .exe to run with")

        print("administrator privileges WITHOUT UAC prompts.")

        print("\nPress Ctrl+C to cancel at any time.")

        print("=" * 70)

        

        if platform.system() != "Windows":

            print("\nERROR: This installer only works on Windows.")

            input("\nPress Enter to exit...")

            return

        

        if not is_admin():

            print("\nAdministrator privileges required for installation.")

            print("Requesting elevation...")

            if not run_as_admin():

                print("\nERROR: Failed to obtain administrator privileges.")

                input("\nPress Enter to exit...")

                return

            return

        

        print("\n✓ Running with administrator privileges")

        

        if create_task_scheduler():

            print("\n" + "=" * 70)

            print("INSTALLATION SUCCESSFUL!")

            print("=" * 70)

            print("\nYou can now launch DNS Auto Changer without UAC prompts by:")

            print("  • Double-clicking 'Launch_DNS_Changer.bat'")

            print("  • Using the desktop shortcut (if created)")

        else:

            print("\n" + "=" * 70)

            print("INSTALLATION FAILED")

            print("=" * 70)

            print("\nPlease check the error messages above.")

        

        input("\nPress Enter to exit...")


    # Execute install mode

    try:

        install_main()

    except KeyboardInterrupt:

        print("\n\nInstallation cancelled by user.")

        input("\nPress Enter to exit...")

    except Exception as e:

        print(f"\n\nUnexpected error: {e}")

        import traceback

        traceback.print_exc()

        input("\nPress Enter to exit...")

    

    sys.exit(0)


# ============================================================================

# RUN MODE - Main DNS Changer Application

# ============================================================================


# All the imports for the main application

import tkinter as tk

from tkinter import ttk, messagebox

import subprocess

import platform

import threading

import time

import ctypes

import json

try:

    from PIL import Image, ImageDraw

    import pystray

    TRAY_AVAILABLE = True

except ImportError:

    TRAY_AVAILABLE = False

    print("Warning: pystray or PIL not available. System tray functionality disabled.")

    print("Install with: pip install pystray pillow")


# Continue with dns_changer.py code...

class DNSChangerApp:

    def __init__(self, root, start_as_icon=False):

        self.root = root

        self.root.title("DNS Auto Changer")

        self.root.geometry("600x700")

        self.root.resizable(False, False)

        

        # Configuration file path (same directory as executable/script)

        if getattr(sys, 'frozen', False):

            self.config_path = os.path.join(os.path.dirname(sys.executable), 'dns_changer_config.json')

        else:

            self.config_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'dns_changer_config.json')

        

        # Predefined DNS servers (must be defined BEFORE load_config)

        self.predefined_dns = {

            "Yandex DNS": ("77.88.8.1", "77.88.8.8"),

            "Level3 DNS 1": ("209.244.0.3", "209.244.0.4"),

            "Level3 DNS 2": ("4.2.2.1", "4.2.2.2"),

            "Level3 DNS 3": ("4.2.2.3", "4.2.2.4"),

            "Level3 DNS 4": ("4.2.2.5", "4.2.2.6"),  # Virgola aggiunta

            "UltraDNS": ("204.69.234.1", "204.74.101.1"),

            "Censurfridns": ("89.233.43.71", "91.239.100.100"),

            "OpenDNS A": ("208.67.222.222", "208.67.220.220"),

            "OpenDNS B": ("208.67.222.220", "208.67.220.222"),

            "Safe DNS": ("195.46.39.39", "195.46.39.40"),

            "Cloudflare DNS": ("1.1.1.1", "1.0.0.1")

        }

        

        # Predefined time intervals (in seconds)

        self.predefined_intervals = {  # Questa linea deve essere correttamente indentata

            "5 seconds": 5,

            "15 seconds": 15,

            "30 seconds": 30,

            "1 minute": 60,

            "3 minutes": 180,

            "5 minutes": 300,

            "15 minutes": 900,

            "30 minutes": 1800,

            "45 minutes": 2700

        }

        

        # Load configuration

        self.config = self.load_config()

        

        # System tray variables

        self.tray_icon = None

        self.start_as_icon_var = tk.BooleanVar(value=start_as_icon or self.config.get('start_as_icon', False))

        self.is_minimized_to_tray = False

        

        # Auto start variable

        self.auto_start_changer_var = tk.BooleanVar(value=self.config.get('auto_start_changer', False))

        

        # Variables

        self.is_running = False

        self.timer_thread = None

        self.remaining_time = 0

        

        # Custom DNS list (merge predefined with user-added)

        self.custom_dns_list = self.config.get('custom_dns_list', {})

        

        # Rotation settings

        self.rotation_mode_var = tk.StringVar(value=self.config.get('rotation_mode', 'single'))

        self.current_rotation_index = 0

        

        # Initialize dns_checkboxes with saved selections

        saved_selections = self.config.get('selected_dns_for_rotation', [])

        self.dns_checkboxes = {name: tk.BooleanVar(value=(name in saved_selections)) 

                               for name in self.get_all_dns().keys()}

        

        self.setup_ui()

    

    def get_all_dns(self):

        """Get combined dictionary of predefined and custom DNS"""

        all_dns = self.predefined_dns.copy()

        all_dns.update(self.custom_dns_list)

        return all_dns

    

    def update_dns_combo(self):

        """Update DNS combobox with all available DNS"""

        all_dns = self.get_all_dns()

        self.dns_combo['values'] = list(all_dns.keys())

        

        current = self.dns_combo.get()

        if current not in all_dns:

            if all_dns:

                self.dns_combo.current(0)

    

    def load_config(self):

        """Load configuration from file"""

        default_config = {

            'start_as_icon': False,

            'selected_dns': 'Yandex DNS',

            'primary_dns': '77.88.8.1',

            'secondary_dns': '77.88.8.8',

            'interval_preset': '1 minute',

            'custom_minutes': 0,

            'custom_seconds': 10,

            'custom_dns_list': {},

            'rotation_mode': 'single',

            'selected_dns_for_rotation': [],

            'auto_start_changer': False

        }

        

        try:

            if os.path.exists(self.config_path):

                with open(self.config_path, 'r') as f:

                    loaded_config = json.load(f)

                    default_config.update(loaded_config)

                    print(f"Configuration loaded from: {self.config_path}")

            else:

                print(f"No configuration file found. Using defaults.")

        except Exception as e:

            print(f"Error loading configuration: {e}")

            print("Using default configuration.")

        

        return default_config

    

    def save_config(self):

        """Save current configuration to file"""

        try:

            config = {

                'start_as_icon': self.start_as_icon_var.get(),

                'selected_dns': self.dns_combo.get(),

                'primary_dns': self.primary_dns.get(),

                'secondary_dns': self.secondary_dns.get(),

                'interval_preset': self.interval_combo.get(),

                'custom_minutes': int(self.minutes_var.get()),

                'custom_seconds': int(self.seconds_var.get()),

                'custom_dns_list': self.custom_dns_list,

                'rotation_mode': self.rotation_mode_var.get(),

                'selected_dns_for_rotation': self.get_selected_dns_for_rotation(),

                'auto_start_changer': self.auto_start_changer_var.get()

            }

            

            with open(self.config_path, 'w') as f:

                json.dump(config, f, indent=4)

            

            print(f"Configuration saved to: {self.config_path}")

            return True

        except Exception as e:

            print(f"Error saving configuration: {e}")

            return False

    

    def setup_ui(self):

        """Setup the user interface"""

        # Main frame

        main_frame = ttk.Frame(self.root, padding="10")

        main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))

        

        # DNS Selection Section

        dns_frame = ttk.LabelFrame(main_frame, text="DNS Server Selection", padding="10")

        dns_frame.grid(row=0, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=5)

        

        # Predefined DNS

        ttk.Label(dns_frame, text="Predefined DNS:").grid(row=0, column=0, sticky=tk.W, pady=5)

        self.dns_combo = ttk.Combobox(dns_frame, values=list(self.get_all_dns().keys()), 

                                      state="readonly", width=30)

        self.dns_combo.grid(row=0, column=1, padx=5, pady=5)

        saved_dns = self.config.get('selected_dns', 'Yandex DNS')

        if saved_dns in self.get_all_dns().keys():

            self.dns_combo.set(saved_dns)

        else:

            self.dns_combo.current(0)

        self.dns_combo.bind("<<ComboboxSelected>>", self.on_dns_selected)

        

        # Add new DNS button

        add_dns_btn = ttk.Button(dns_frame, text="Add new DNS", command=self.open_add_dns_dialog, width=15)

        add_dns_btn.grid(row=0, column=2, padx=5, pady=5)

        

        # Custom DNS

        ttk.Label(dns_frame, text="Primary DNS:").grid(row=1, column=0, sticky=tk.W, pady=5)

        self.primary_dns = ttk.Entry(dns_frame, width=32)

        self.primary_dns.grid(row=1, column=1, padx=5, pady=5)

        self.primary_dns.insert(0, self.config.get('primary_dns', '77.88.8.1'))

        

        ttk.Label(dns_frame, text="Secondary DNS:").grid(row=2, column=0, sticky=tk.W, pady=5)

        self.secondary_dns = ttk.Entry(dns_frame, width=32)

        self.secondary_dns.grid(row=2, column=1, padx=5, pady=5)

        self.secondary_dns.insert(0, self.config.get('secondary_dns', '77.88.8.8'))

        

        # Time Interval Section

        interval_frame = ttk.LabelFrame(main_frame, text="Time Interval", padding="10")

        interval_frame.grid(row=1, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=5)

        

        ttk.Label(interval_frame, text="Predefined:").grid(row=0, column=0, sticky=tk.W, pady=5)

        self.interval_combo = ttk.Combobox(interval_frame, 

                                          values=list(self.predefined_intervals.keys()),

                                          state="readonly", width=30)

        self.interval_combo.grid(row=0, column=1, padx=5, pady=5)

        saved_interval = self.config.get('interval_preset', '1 minute')

        if saved_interval in self.predefined_intervals.keys():

            self.interval_combo.set(saved_interval)

        else:

            self.interval_combo.current(3)

        self.interval_combo.bind("<<ComboboxSelected>>", self.on_interval_selected)

        

        custom_frame = ttk.Frame(interval_frame)

        custom_frame.grid(row=1, column=0, columnspan=2, pady=5)

        

        ttk.Label(custom_frame, text="Custom (Min:Sec):").pack(side=tk.LEFT, padx=5)

        

        self.minutes_var = tk.StringVar(value=str(self.config.get('custom_minutes', 0)))

        minutes_spinbox = ttk.Spinbox(custom_frame, from_=0, to=999, width=5, 

                                     textvariable=self.minutes_var)

        minutes_spinbox.pack(side=tk.LEFT, padx=2)

        

        ttk.Label(custom_frame, text=":").pack(side=tk.LEFT)

        

        self.seconds_var = tk.StringVar(value=str(self.config.get('custom_seconds', 10)))

        seconds_spinbox = ttk.Spinbox(custom_frame, from_=0, to=59, width=5,

                                     textvariable=self.seconds_var)

        seconds_spinbox.pack(side=tk.LEFT, padx=2)

        

        # DNS Rotation Mode Section

        rotation_frame = ttk.LabelFrame(main_frame, text="DNS Rotation Mode", padding="10")

        rotation_frame.grid(row=2, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=5)

        

        mode_frame = ttk.Frame(rotation_frame)

        mode_frame.grid(row=0, column=0, columnspan=2, sticky=tk.W, pady=5)

        

        ttk.Radiobutton(mode_frame, text="Single DNS", variable=self.rotation_mode_var, 

                       value="single", command=self.on_rotation_mode_changed).pack(side=tk.LEFT, padx=5)

        ttk.Radiobutton(mode_frame, text="Sequential Rotation", variable=self.rotation_mode_var, 

                       value="sequential", command=self.on_rotation_mode_changed).pack(side=tk.LEFT, padx=5)

        ttk.Radiobutton(mode_frame, text="Random Rotation", variable=self.rotation_mode_var, 

                       value="random", command=self.on_rotation_mode_changed).pack(side=tk.LEFT, padx=5)

        

        manage_btn = ttk.Button(rotation_frame, text="Manage DNS Selection", 

                               command=self.open_dns_selection_dialog, width=20)

        manage_btn.grid(row=1, column=0, columnspan=2, pady=5)

        

        # Control buttons

        control_frame = ttk.Frame(main_frame)

        control_frame.grid(row=3, column=0, columnspan=2, pady=10)

        

        self.start_btn = ttk.Button(control_frame, text="Start", command=self.start_timer, width=15)

        self.start_btn.pack(side=tk.LEFT, padx=5)

        

        self.stop_btn = ttk.Button(control_frame, text="Stop", command=self.stop_timer,

                                   width=15, state=tk.DISABLED)

        self.stop_btn.pack(side=tk.LEFT, padx=5)

        

        self.apply_now_btn = ttk.Button(control_frame, text="Apply DNS Now", 

                                        command=self.apply_dns_now, width=15)

        self.apply_now_btn.pack(side=tk.LEFT, padx=5)

        

        # System Tray Controls

        tray_frame = ttk.Frame(main_frame)

        tray_frame.grid(row=4, column=0, columnspan=2, pady=5)

        

        if TRAY_AVAILABLE:

            self.reduce_icon_btn = ttk.Button(tray_frame, text="Reduce as Icon", 

                                            command=self.minimize_to_tray, width=15)

            self.reduce_icon_btn.pack(side=tk.LEFT, padx=5)

            

            start_as_icon_check = ttk.Checkbutton(tray_frame, text="Start as Icon", 

                                                 variable=self.start_as_icon_var,

                                                 command=self.on_start_as_icon_changed)

            start_as_icon_check.pack(side=tk.LEFT, padx=5)

        else:

            info_label = ttk.Label(tray_frame, text="System tray disabled (install pystray & pillow)", 

                                  foreground="gray")

            info_label.pack(side=tk.LEFT, padx=5)

        

        auto_start_check = ttk.Checkbutton(tray_frame, text="Auto Start Changer", 

                                          variable=self.auto_start_changer_var,

                                          command=self.on_auto_start_changed)

        auto_start_check.pack(side=tk.LEFT, padx=5)

        

        # Status Section

        status_frame = ttk.LabelFrame(main_frame, text="Status", padding="10")

        status_frame.grid(row=5, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=5)

        

        self.status_label = ttk.Label(status_frame, text="Status: Ready", foreground="blue")

        self.status_label.pack(anchor=tk.W)

        

        self.timer_label = ttk.Label(status_frame, text="Next change in: --:--", foreground="green")

        self.timer_label.pack(anchor=tk.W, pady=5)

        

        # Log Section

        log_frame = ttk.LabelFrame(main_frame, text="Log", padding="5")

        log_frame.grid(row=6, column=0, columnspan=2, sticky=(tk.W, tk.E, tk.N, tk.S), pady=5)

        

        scrollbar = ttk.Scrollbar(log_frame)

        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

        

        self.log_text = tk.Text(log_frame, height=8, width=70, yscrollcommand=scrollbar.set)

        self.log_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)

        scrollbar.config(command=self.log_text.yview)

        

        # Initialize with first DNS

        self.on_dns_selected(None)

        

        # Set up window protocol for close button

        self.root.protocol("WM_DELETE_WINDOW", self.on_closing)

        

        # Start as icon if requested

        if self.start_as_icon_var.get() and TRAY_AVAILABLE:

            self.root.after(100, self.minimize_to_tray)

        

        # Auto start changer if enabled

        if self.auto_start_changer_var.get():

            self.root.after(500, self.start_timer)

            self.log("Auto Start Changer: Enabled - Timer will start automatically")

    

    def create_tray_image(self):

        """Create an image for the system tray icon"""

        width = 64

        height = 64

        image = Image.new('RGB', (width, height), color='white')

        draw = ImageDraw.Draw(image)

        

        draw.ellipse([4, 4, width-4, height-4], fill='#2ecc71', outline='#27ae60', width=2)

        draw.text((width//2 - 10, height//2 - 12), 'D', fill='white')

        

        return image

    

    def create_tray_menu(self):

        """Create the system tray menu"""

        return pystray.Menu(

            pystray.MenuItem("Show", self.show_window, default=True),

            pystray.MenuItem("Apply DNS Now", self.tray_apply_dns),

            pystray.MenuItem("Start Auto-Change", self.tray_start_timer, 

                           visible=lambda item: not self.is_running),

            pystray.MenuItem("Stop Auto-Change", self.tray_stop_timer,

                           visible=lambda item: self.is_running),

            pystray.Menu.SEPARATOR,

            pystray.MenuItem("Exit", self.tray_exit)

        )

    

    def minimize_to_tray(self):

        """Minimize the window to system tray"""

        if not TRAY_AVAILABLE:

            messagebox.showwarning("Not Available", 

                                 "System tray functionality requires pystray and pillow.\n"

                                 "Install with: pip install pystray pillow")

            return

        

        self.is_minimized_to_tray = True

        self.root.withdraw()

        

        if self.tray_icon is None:

            image = self.create_tray_image()

            self.tray_icon = pystray.Icon(

                "DNS_Auto_Changer",

                image,

                "DNS Auto Changer",

                menu=self.create_tray_menu()

            )

            

            tray_thread = threading.Thread(target=self.tray_icon.run, daemon=True)

            tray_thread.start()

        

        self.log("Minimized to system tray")

    

    def show_window(self, icon=None, item=None):

        """Show the window from system tray"""

        self.is_minimized_to_tray = False

        self.root.deiconify()

        self.root.lift()

        self.root.focus_force()

        self.log("Restored from system tray")

    

    def tray_apply_dns(self, icon=None, item=None):

        """Apply DNS from tray menu"""

        self.root.after(0, self.apply_dns_now)

    

    def tray_start_timer(self, icon=None, item=None):

        """Start timer from tray menu"""

        self.root.after(0, self.start_timer)

    

    def tray_stop_timer(self, icon=None, item=None):

        """Stop timer from tray menu"""

        self.root.after(0, self.stop_timer)

    

    def tray_exit(self, icon=None, item=None):

        """Exit application from tray menu"""

        if self.tray_icon:

            self.tray_icon.stop()

        self.root.after(0, self.root.quit)

    

    def on_closing(self):

        """Handle window close button"""

        self.save_config()

        

        if TRAY_AVAILABLE and messagebox.askyesno("Minimize to Tray?", 

                                                   "Minimize to system tray instead of closing?\n\n"

                                                   "Click 'No' to exit completely."):

            self.minimize_to_tray()

        else:

            if self.tray_icon:

                self.tray_icon.stop()

            self.root.quit()

    

    def on_dns_selected(self, event):

        """Update custom DNS fields when predefined DNS is selected"""

        selected = self.dns_combo.get()

        all_dns = self.get_all_dns()

        if selected in all_dns:

            primary, secondary = all_dns[selected]

            self.primary_dns.delete(0, tk.END)

            self.primary_dns.insert(0, primary)

            self.secondary_dns.delete(0, tk.END)

            self.secondary_dns.insert(0, secondary)

        self.save_config()

    

    def on_interval_selected(self, event):

        """Update custom time when predefined interval is selected"""

        selected = self.interval_combo.get()

        if selected in self.predefined_intervals:

            total_seconds = self.predefined_intervals[selected]

            minutes = total_seconds // 60

            seconds = total_seconds % 60

            self.minutes_var.set(str(minutes))

            self.seconds_var.set(str(seconds))

        self.save_config()

    

    def on_start_as_icon_changed(self):

        """Handle start as icon checkbox change"""

        self.save_config()

        self.log(f"Start as Icon: {'Enabled' if self.start_as_icon_var.get() else 'Disabled'}")

    

    def on_auto_start_changed(self):

        """Handle auto start changer checkbox change"""

        self.save_config()

        self.log(f"Auto Start Changer: {'Enabled' if self.auto_start_changer_var.get() else 'Disabled'}")

    

    def on_rotation_mode_changed(self):

        """Handle rotation mode change"""

        mode = self.rotation_mode_var.get()

        mode_names = {'single': 'Single DNS', 'sequential': 'Sequential Rotation', 'random': 'Random Rotation'}

        self.log(f"Rotation mode changed to: {mode_names.get(mode, mode)}")

        self.save_config()

    

    def open_add_dns_dialog(self):

        """Open dialog to add a new custom DNS"""

        dialog = tk.Toplevel(self.root)

        dialog.title("Add New DNS")

        dialog.geometry("400x200")

        dialog.resizable(False, False)

        dialog.transient(self.root)

        dialog.grab_set()

        

        dialog.update_idletasks()

        x = self.root.winfo_x() + (self.root.winfo_width() // 2) - (dialog.winfo_width() // 2)

        y = self.root.winfo_y() + (self.root.winfo_height() // 2) - (dialog.winfo_height() // 2)

        dialog.geometry(f"+{x}+{y}")

        

        ttk.Label(dialog, text="DNS Name:").grid(row=0, column=0, padx=10, pady=10, sticky=tk.W)

        name_entry = ttk.Entry(dialog, width=30)

        name_entry.grid(row=0, column=1, padx=10, pady=10)

        name_entry.focus()

        

        ttk.Label(dialog, text="Primary DNS:").grid(row=1, column=0, padx=10, pady=10, sticky=tk.W)

        primary_entry = ttk.Entry(dialog, width=30)

        primary_entry.grid(row=1, column=1, padx=10, pady=10)

        

        ttk.Label(dialog, text="Secondary DNS:").grid(row=2, column=0, padx=10, pady=10, sticky=tk.W)

        secondary_entry = ttk.Entry(dialog, width=30)

        secondary_entry.grid(row=2, column=1, padx=10, pady=10)

        

        def on_ok():

            name = name_entry.get().strip()

            primary = primary_entry.get().strip()

            secondary = secondary_entry.get().strip()

            

            if not name:

                messagebox.showerror("Error", "DNS name cannot be empty", parent=dialog)

                return

            

            if not primary or not secondary:

                messagebox.showerror("Error", "Both DNS addresses must be specified", parent=dialog)

                return

            

            if name in self.get_all_dns():

                messagebox.showerror("Error", f"DNS with name '{name}' already exists", parent=dialog)

                return

            

            self.custom_dns_list[name] = (primary, secondary)

            self.update_dns_combo()

            self.dns_combo.set(name)

            self.on_dns_selected(None)

            self.save_config()

            

            self.log(f"Added new DNS: {name} ({primary}, {secondary})")

            dialog.destroy()

        

        def on_cancel():

            dialog.destroy()

        

        button_frame = ttk.Frame(dialog)

        button_frame.grid(row=3, column=0, columnspan=2, pady=20)

        

        ttk.Button(button_frame, text="OK", command=on_ok, width=10).pack(side=tk.LEFT, padx=5)

        ttk.Button(button_frame, text="Cancel", command=on_cancel, width=10).pack(side=tk.LEFT, padx=5)

        

        dialog.bind('<Return>', lambda e: on_ok())

        dialog.bind('<Escape>', lambda e: on_cancel())

    

    def open_dns_selection_dialog(self):

        """Open dialog to select DNS servers for rotation"""

        dialog = tk.Toplevel(self.root)

        dialog.title("Select DNS for Rotation")

        dialog.geometry("400x400")

        dialog.resizable(False, False)

        dialog.transient(self.root)

        dialog.grab_set()

        

        dialog.update_idletasks()

        x = self.root.winfo_x() + (self.root.winfo_width() // 2) - (dialog.winfo_width() // 2)

        y = self.root.winfo_y() + (self.root.winfo_height() // 2) - (dialog.winfo_height() // 2)

        dialog.geometry(f"+{x}+{y}")

        

        ttk.Label(dialog, text="Select DNS servers to include in rotation:", 

                 font=('Arial', 10, 'bold')).pack(pady=10)

        

        canvas = tk.Canvas(dialog, height=250)

        scrollbar = ttk.Scrollbar(dialog, orient="vertical", command=canvas.yview)

        scrollable_frame = ttk.Frame(canvas)

        

        scrollable_frame.bind(

            "<Configure>",

            lambda e: canvas.configure(scrollregion=canvas.bbox("all"))

        )

        

        canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")

        canvas.configure(yscrollcommand=scrollbar.set)

        

        canvas.pack(side="left", fill="both", expand=True, padx=10)

        scrollbar.pack(side="right", fill="y")

        

        all_dns = self.get_all_dns()

        

        for dns_name in all_dns.keys():

            if dns_name not in self.dns_checkboxes:

                self.dns_checkboxes[dns_name] = tk.BooleanVar(value=False)

        

        for dns_name in sorted(all_dns.keys()):

            var = self.dns_checkboxes[dns_name]

            primary, secondary = all_dns[dns_name]

            ttk.Checkbutton(scrollable_frame, 

                          text=f"{dns_name} ({primary})", 

                          variable=var).pack(anchor=tk.W, padx=10, pady=2)

        

        def on_ok():

            selected = self.get_selected_dns_for_rotation()

            

            if self.rotation_mode_var.get() != 'single' and len(selected) < 2:

                messagebox.showwarning("Warning", 

                                     "Please select at least 2 DNS servers for rotation mode",

                                     parent=dialog)

                return

            

            self.save_config()

            

            mode = self.rotation_mode_var.get()

            if mode == 'single':

                self.log(f"DNS selection saved (not used in Single DNS mode)")

            else:

                self.log(f"Selected {len(selected)} DNS for rotation: {', '.join(selected)}")

            

            dialog.destroy()

        

        def on_cancel():

            dialog.destroy()

        

        button_frame = ttk.Frame(dialog)

        button_frame.pack(pady=10)

        

        ttk.Button(button_frame, text="OK", command=on_ok, width=10).pack(side=tk.LEFT, padx=5)

        ttk.Button(button_frame, text="Cancel", command=on_cancel, width=10).pack(side=tk.LEFT, padx=5)

        

        dialog.bind('<Return>', lambda e: on_ok())

        dialog.bind('<Escape>', lambda e: on_cancel())

    

    def get_selected_dns_for_rotation(self):

        """Get list of DNS names selected for rotation"""

        if not hasattr(self, 'dns_checkboxes') or not self.dns_checkboxes:

            return self.config.get('selected_dns_for_rotation', [])

        

        selected = [name for name, var in self.dns_checkboxes.items() if var.get()]

        return selected

    

    def get_next_dns_for_rotation(self):

        """Get the next DNS to apply based on rotation mode"""

        mode = self.rotation_mode_var.get()

        

        if mode == 'single':

            selected = self.dns_combo.get()

            all_dns = self.get_all_dns()

            if selected in all_dns:

                return selected, all_dns[selected]

            return None, None

        

        selected_dns_list = self.get_selected_dns_for_rotation()

        

        if not selected_dns_list:

            self.log("Warning: No DNS selected for rotation. Using current selection.")

            selected = self.dns_combo.get()

            all_dns = self.get_all_dns()

            if selected in all_dns:

                return selected, all_dns[selected]

            return None, None

        

        all_dns = self.get_all_dns()

        

        if mode == 'sequential':

            dns_name = selected_dns_list[self.current_rotation_index % len(selected_dns_list)]

            self.current_rotation_index += 1

            return dns_name, all_dns[dns_name]

        

        elif mode == 'random':

            import random

            dns_name = random.choice(selected_dns_list)

            return dns_name, all_dns[dns_name]

        

        return None, None

    

    def get_interval_seconds(self):

        """Get the total interval in seconds from custom input"""

        try:

            minutes = int(self.minutes_var.get())

            seconds = int(self.seconds_var.get())

            total = minutes * 60 + seconds

            if total <= 0:

                raise ValueError("Interval must be greater than 0")

            return total

        except ValueError as e:

            messagebox.showerror("Error", f"Invalid time interval: {e}")

            return None

    

    def log(self, message):

        """Add message to log (both GUI and console)"""

        timestamp = time.strftime("%H:%M:%S")

        log_message = f"[{timestamp}] {message}"

        

        self.log_text.insert(tk.END, f"{log_message}\n")

        self.log_text.see(tk.END)

        self.root.update()

        

        print(log_message)

    

    def get_windows_interfaces(self):

        """Get all network interfaces on Windows"""

        try:

            print("\n--- Scanning for network interfaces ---")

            

            result = subprocess.run(

                ['netsh', 'interface', 'ip', 'show', 'config'],

                capture_output=True, text=True, shell=True, encoding='utf-8'

            )

            

            interfaces = []

            lines = result.stdout.split('\n')

            

            for line in lines:

                if 'Configuration for interface' in line or 'Configurazione per' in line:

                    if '"' in line:

                        start = line.index('"') + 1

                        end = line.index('"', start)

                        interface_name = line[start:end]

                        interfaces.append(interface_name)

                        print(f"  Found interface: {interface_name}")

            

            if not interfaces:

                print("  No interfaces found with IP config, trying alternative method...")

                result = subprocess.run(

                    ['netsh', 'interface', 'show', 'interface'],

                    capture_output=True, text=True, shell=True, encoding='utf-8'

                )

                

                lines = result.stdout.split('\n')

                for line in lines[3:]:

                    if line.strip():

                        parts = line.split()

                        if len(parts) >= 4:

                            name = ' '.join(parts[3:])

                            interfaces.append(name)

                            print(f"  Found interface: {name}")

            

            print(f"--- Total interfaces found: {len(interfaces)} ---\n")

            return interfaces

        except Exception as e:

            error_msg = f"Error getting interfaces: {e}"

            self.log(error_msg)

            print(f"ERROR: {error_msg}")

            return []

    

    def set_dns_windows(self, primary, secondary):

        """Set DNS on Windows for all interfaces"""

        print(f"\n{'='*60}")

        print(f"Starting DNS application process")

        print(f"Primary DNS: {primary}")

        print(f"Secondary DNS: {secondary}")

        print(f"{'='*60}\n")

        

        interfaces = self.get_windows_interfaces()

        

        if not interfaces:

            msg = "No network interfaces found"

            self.log(msg)

            print(f"ERROR: {msg}")

            return False

        

        self.log(f"Found {len(interfaces)} network interface(s)")

        

        success_count = 0

        failed_count = 0

        

        for i, interface in enumerate(interfaces, 1):

            try:

                print(f"\n[{i}/{len(interfaces)}] Processing: {interface}")

                self.log(f"Applying DNS to: {interface}")

                

                print(f"  → Setting primary DNS ({primary})...")

                result1 = subprocess.run(

                    ['netsh', 'interface', 'ip', 'set', 'dns', f'name={interface}', 'static', primary],

                    capture_output=True, text=True, shell=True, encoding='utf-8'

                )

                

                print(f"  → Setting secondary DNS ({secondary})...")

                result2 = subprocess.run(

                    ['netsh', 'interface', 'ip', 'add', 'dns', f'name={interface}', secondary, 'index=2'],

                    capture_output=True, text=True, shell=True, encoding='utf-8'

                )

                

                if result1.returncode == 0 or result2.returncode == 0:

                    success_msg = f"  ✓ Success on {interface}"

                    self.log(success_msg)

                    print(f"  ✓ SUCCESS")

                    success_count += 1

                else:

                    fail_msg = f"  ✗ Failed on {interface}"

                    self.log(fail_msg)

                    print(f"  ✗ FAILED")

                    if result1.stderr:

                        print(f"     Error: {result1.stderr.strip()}")

                    if result2.stderr:

                        print(f"     Error: {result2.stderr.strip()}")

                    failed_count += 1

                    

            except Exception as e:

                error_msg = f"  ✗ Error on {interface}: {e}"

                self.log(error_msg)

                print(f"  ✗ EXCEPTION: {e}")

                failed_count += 1

        

        summary = f"DNS application completed: {success_count} succeeded, {failed_count} failed"

        self.log(summary)

        print(f"\n{'='*60}")

        print(f"SUMMARY: {success_count} succeeded, {failed_count} failed")

        print(f"{'='*60}\n")

        

        return success_count > 0

    

    def set_dns_linux(self, primary, secondary):

        """Set DNS on Linux"""

        try:

            dns_config = f"nameserver {primary}\nnameserver {secondary}\n"

            

            with open('/etc/resolv.conf', 'w') as f:

                f.write(dns_config)

            

            return True

        except PermissionError:

            self.log("Error: Need root/sudo permissions to change DNS on Linux")

            return False

        except Exception as e:

            self.log(f"Error setting DNS on Linux: {e}")

            return False

    

    def set_dns_mac(self, primary, secondary):

        """Set DNS on macOS"""

        try:

            result = subprocess.run(

                ['networksetup', '-listallnetworkservices'],

                capture_output=True, text=True

            )

            

            services = [s for s in result.stdout.split('\n') if s and not s.startswith('*')]

            

            if services:

                service = services[0]

                subprocess.run(

                    ['networksetup', '-setdnsservers', service, primary, secondary],

                    check=True

                )

                return True

            else:

                self.log("No network service found")

                return False

        except Exception as e:

            self.log(f"Error setting DNS on macOS: {e}")

            return False

    

    def apply_dns(self):

        """Apply DNS settings based on OS and rotation mode"""

        mode = self.rotation_mode_var.get()

        

        if mode == 'single':

            primary = self.primary_dns.get().strip()

            secondary = self.secondary_dns.get().strip()

            dns_name = self.dns_combo.get()

        else:

            dns_name, dns_servers = self.get_next_dns_for_rotation()

            if dns_servers is None:

                self.log("Error: Could not determine DNS for rotation")

                return False

            primary, secondary = dns_servers

        

        if not primary or not secondary:

            self.log("Error: Both primary and secondary DNS must be specified")

            return False

        

        system = platform.system()

        self.log(f"Applying DNS: {dns_name} ({primary}, {secondary})")

        

        success = False

        if system == "Windows":

            success = self.set_dns_windows(primary, secondary)

        elif system == "Linux":

            success = self.set_dns_linux(primary, secondary)

        elif system == "Darwin":

            success = self.set_dns_mac(primary, secondary)

        else:

            self.log(f"Unsupported operating system: {system}")

            return False

        

        if success:

            self.log("DNS applied successfully!")

            self.status_label.config(text=f"Status: DNS Applied ({dns_name})", foreground="green")

        else:

            self.status_label.config(text="Status: DNS Application Failed", foreground="red")

        

        return success

    

    def apply_dns_now(self):

        """Apply DNS immediately without starting timer"""

        self.apply_dns()

    

    def timer_worker(self, interval):

        """Worker thread for timer"""

        while self.is_running:

            self.apply_dns()

            

            self.remaining_time = interval

            while self.remaining_time > 0 and self.is_running:

                minutes = self.remaining_time // 60

                seconds = self.remaining_time % 60

                self.timer_label.config(text=f"Next change in: {minutes:02d}:{seconds:02d}")

                self.root.update()

                time.sleep(1)

                self.remaining_time -= 1

    

    def start_timer(self):

        """Start the DNS auto-change timer"""

        interval = self.get_interval_seconds()

        if interval is None:

            return

        

        self.is_running = True

        self.start_btn.config(state=tk.DISABLED)

        self.stop_btn.config(state=tk.NORMAL)

        self.status_label.config(text="Status: Running", foreground="green")

        

        self.log(f"Started auto-change with {interval} seconds interval")

        

        self.timer_thread = threading.Thread(target=self.timer_worker, args=(interval,), daemon=True)

        self.timer_thread.start()

    

    def stop_timer(self):

        """Stop the DNS auto-change timer"""

        self.is_running = False

        self.start_btn.config(state=tk.NORMAL)

        self.stop_btn.config(state=tk.DISABLED)

        self.status_label.config(text="Status: Stopped", foreground="orange")

        self.timer_label.config(text="Next change in: --:--")

        self.log("Stopped auto-change")


def is_admin():

    """Check if the program is running with administrator privileges"""

    try:

        if platform.system() == "Windows":

            return ctypes.windll.shell32.IsUserAnAdmin()

        else:

            return os.geteuid() == 0

    except:

        return False


def run_as_admin():

    """Restart the program with administrator privileges"""

    try:

        if platform.system() == "Windows":

            if getattr(sys, 'frozen', False):

                application_path = sys.executable

                params = ' '.join(sys.argv[1:])

            else:

                application_path = sys.executable

                script = os.path.abspath(__file__)

                params = f'"{script}"' + ' '.join(sys.argv[1:])

            

            ret = ctypes.windll.shell32.ShellExecuteW(

                None, 

                "runas",

                application_path,

                params,

                None, 

                1

            )

            

            if ret > 32:

                sys.exit(0)

            else:

                return False

        else:

            print("Please run this program with sudo:")

            print(f"sudo python3 {sys.argv[0]}")

            sys.exit(1)

        

        return True

    except Exception as e:

        print(f"Failed to elevate privileges: {e}")

        messagebox.showerror("Error", 

            "Failed to request administrator privileges.\n"

            "Please run the program as administrator manually.\n\n"

            f"Error: {e}")

        return False


def main():

    if not is_admin():

        print("=" * 60)

        print("DNS Auto Changer Tool")

        print("=" * 60)

        print("Administrator privileges required.")

        print("Requesting elevation...")

        print("=" * 60)

        

        if not run_as_admin():

            print("\nFailed to obtain administrator privileges.")

            print("Please run the program manually as administrator.")

            input("\nPress Enter to exit...")

            sys.exit(1)

        return

    

    print("=" * 60)

    print("DNS Auto Changer Tool")

    print("Running with administrator privileges ✓")

    print("=" * 60)

    print("Starting application...")

    print("All operations will be logged below:")

    print("-" * 60)

    

    start_as_icon = "--tray" in sys.argv or "--icon" in sys.argv

    

    root = tk.Tk()

    app = DNSChangerApp(root, start_as_icon=start_as_icon)

    root.mainloop()


if __name__ == "__main__":

    main()

Commenti

Post popolari in questo blog

No Man's Sky Similar Games

Tower Defense Games

Auto Copy Folder - Source Code