Rotator.py

Rotator.py

-
import subprocess
import time
import threading
import requests
import tkinter as tk
from tkinter import messagebox
from stem import Signal
from stem.control import Controller
import traceback
import os
 
# Настройки
TOR_PATH = r"\tor.exe"               # путь к tor.exe
TOR_RC = r"\torrc"                 # путь к torrc
CLASH_PATH = r"Clash for Windows.exe"
CONTROL_PASSWORD = ""               # пароль ControlPort (torrc)
CONTROL_PORT = 9061                # ControlPort из torrc
CLASH_PORT = 7890                 # локальный порт Clash (обычно 7890)
DEFAULT_INTERVAL = 60               # секунд
CREATE_NO_WINDOW = 0x08000000
 
tor_process = None
clash_process = None
enabled = True
interval_sec = DEFAULT_INTERVAL
 
# Управление системным прокси
def enable_system_proxy(port):
  try:
    subprocess.run([
      "reg", "add",
      r"HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings",
      "/v", "ProxyEnable", "/t", "REG_DWORD", "/d", "1", "/f"
    ], check=True)
    subprocess.run([
      "reg", "add",
      r"HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings",
      "/v", "ProxyServer", "/t", "REG_SZ", "/d", f"127.0.0.1:{port}", "/f"
    ], check=True)
  except Exception as e:
    print("Ошибка включения системного прокси:", e)
    traceback.print_exc()
 
def disable_system_proxy():
  try:
    subprocess.run([
      "reg", "add",
      r"HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings",
      "/v", "ProxyEnable", "/t", "REG_DWORD", "/d", "0", "/f"
    ], check=True)
  except Exception as e:
    print("Ошибка выключения системного прокси:", e)
    traceback.print_exc()
 
# Управление процессами
def start_tor():
  global tor_process
  if not tor_process or tor_process.poll() is not None:
    tor_process = subprocess.Popen(
      [TOR_PATH, "-f", TOR_RC],
      creationflags=subprocess.CREATE_NO_WINDOW
    )
 
def stop_tor():
  global tor_process
  if tor_process and tor_process.poll() is None:
    tor_process.terminate()
    tor_process = None
 
def start_clash():
  global clash_process
  if not clash_process or clash_process.poll() is not None:
    clash_process = subprocess.Popen(
      [CLASH_PATH],
      creationflags=CREATE_NO_WINDOW
    )
    enable_system_proxy(CLASH_PORT)
 
def stop_clash():
  global clash_process
  if clash_process and clash_process.poll() is None:
    clash_process.terminate()
    clash_process = None
  disable_system_proxy()
 
# --- Получение IP через Clash ---
def get_ip_via_clash():
  proxies = {"http": f"http://127.0.0.1:{CLASH_PORT}",
        "https": f"http://127.0.0.1:{CLASH_PORT}"}
  try:
    r = requests.get("https://api.ipify.org", proxies=proxies, timeout=10)
    return r.text.strip()
  except Exception:
    return "—"
 
# Смена IP
def change_ip():
  try:
    with Controller.from_port(port=CONTROL_PORT) as c:
      c.authenticate(password=CONTROL_PASSWORD)
      c.signal(Signal.NEWNYM)
    new_ip = get_ip_via_clash()
    root.after(0, lambda: ip_var.set("Текущий IP: " + new_ip))
  except Exception as e:
    root.after(0, lambda: ip_var.set("Ошибка смены IP"))
    print("change_ip error:", e)
    traceback.print_exc()
 
# --- GUI ---
root = tk.Tk()
root.title("Tor + Clash Rotator")
root.geometry("480x220")
 
ip_var = tk.StringVar(value="Текущий IP: —")
interval_var = tk.StringVar(value=str(DEFAULT_INTERVAL))
state_var = tk.StringVar(value="Ротация: Включена")
 
ip_label = tk.Label(root, textvariable=ip_var, font=("Segoe UI", 10))
ip_label.pack(pady=6)
 
state_label = tk.Label(root, textvariable=state_var, font=("Segoe UI", 11), fg="green")
state_label.pack()
 
frame = tk.Frame(root)
frame.pack(pady=6)
 
def toggle():
  global enabled
  enabled = not enabled
  if enabled:
    start_tor()
    start_clash()
    state_var.set("Ротация: Включена")
    state_label.config(fg="green")
  else:
    stop_tor()
    stop_clash()
    state_var.set("Ротация: Выключена")
    state_label.config(fg="red")
 
def apply_interval():
  global interval_sec
  try:
    v = int(interval_var.get())
    if 3 <= v <= 600:
      interval_sec = v
      messagebox.showinfo("OK", f"Интервал установлен: {interval_sec} сек.")
    else:
      messagebox.showwarning("Ошибка", "Интервал от 3 до 600 секунд.")
  except:
    messagebox.showwarning("Ошибка", "Введите целое число секунд.")
 
tk.Button(frame, text="Вкл/Выкл ротацию", width=16, command=toggle).grid(row=0, column=0, padx=6)
tk.Label(frame, text="Интервал (сек):").grid(row=0, column=1, padx=6)
tk.Entry(frame, width=6, textvariable=interval_var).grid(row=0, column=2)
tk.Button(frame, text="Установить", command=apply_interval).grid(row=0, column=3, padx=6)
 
tk.Button(root, text="Сменить IP сейчас", command=change_ip).pack(pady=6)
 
# Фоновая ротация
def rotator_thread():
  global enabled
  while True:
    if enabled:
      change_ip()
    time.sleep(interval_sec)
 
t = threading.Thread(target=rotator_thread, daemon=True)
t.start()
 
def on_close():
  stop_tor()
  stop_clash()
  root.destroy()
 
root.protocol("WM_DELETE_WINDOW", on_close)
 
# Автозапуск
start_tor()
start_clash()
change_ip()
 
root.mainloop()

Report Page