From 9d511650f6d043e128c41d8d86a9e3e6bcad4e23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Deniz=20=C5=9Eafak?= <39929354+denizsafak@users.noreply.github.com> Date: Mon, 22 Jul 2024 11:08:46 +0300 Subject: [PATCH] v1.82 --- main/HotspotAutoLogin.pyw | 614 ++++++++++++++++++++++++++++++++++ main/config.json | 38 +++ main/icon.ico | Bin 0 -> 3782 bytes main/last_known_directory.txt | 1 + main/requirements.txt | 4 + run.bat | 179 ++++++---- 6 files changed, 780 insertions(+), 56 deletions(-) create mode 100644 main/HotspotAutoLogin.pyw create mode 100644 main/config.json create mode 100644 main/icon.ico create mode 100644 main/last_known_directory.txt create mode 100644 main/requirements.txt diff --git a/main/HotspotAutoLogin.pyw b/main/HotspotAutoLogin.pyw new file mode 100644 index 0000000..13ce378 --- /dev/null +++ b/main/HotspotAutoLogin.pyw @@ -0,0 +1,614 @@ +import json +import os +import requests +import time +import subprocess +import pystray +import threading +import tkinter as tk +from tkinter import Text, Scrollbar +from PIL import Image +from collections import deque +from datetime import datetime +import dns.resolver +dns.resolver.override_system_resolver(dns.resolver.Resolver()) +dns.resolver.default_resolver = dns.resolver.Resolver() +from urllib3.exceptions import InsecureRequestWarning +requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning) + +# Change the current working directory to the script's directory +script_dir = os.path.dirname(os.path.abspath(__file__)) +os.chdir(script_dir) + +# Program Information +program_name = "HotspotAutoLogin" +version = "v1.82" +github_link = "https://github.com/denizsafak/HotspotAutoLogin" + +# Function to center a window on the screen +def center_window(window, width, height): + screen_width = window.winfo_screenwidth() + screen_height = window.winfo_screenheight() + x = (screen_width - width) // 2 + y = (screen_height - height) // 2 + window.geometry(f"{width}x{height}+{x}+{y}") + +def run_profile(): + global selected_profile + refresh_profile_details() + selected_index = listbox.curselection() + if selected_index: + selected_index = int(selected_index[0]) + selected_profile = profiles[selected_index] + root.destroy() + +# Define a function to update the profile details +def update_profile_details(event): + global selected_profile + selected_index = listbox.curselection() + if selected_index: + selected_index = int(selected_index[0]) + selected_profile = profiles[selected_index] + profile_name.config(text=f"Selected Profile: {selected_profile['name']}") + profile_details.config(state=tk.NORMAL) + profile_details.delete(1.0, tk.END) + # Insert profile details with all variables except "name"WWWW + profile_details.tag_configure("bold", font=("Courier New", 10, "bold"), foreground="#08872a") + for key, value in selected_profile.items(): + if key != 'name': # Skip displaying the "name" variable + profile_details.insert(tk.END, f"{key}: ", "bold") + profile_details.insert(tk.END, f"{value}\n") + profile_details.config(state=tk.DISABLED) + else: + profile_name.config(text="Selected Profile: ") + profile_details.config(state=tk.NORMAL) + profile_details.delete(1.0, tk.END) + profile_details.config(state=tk.DISABLED) + +# Function to refresh the profile details +def refresh_profile_details(): + global profiles + # Get the index of the currently selected profile + selected_index = listbox.curselection() + selected_index = int(selected_index[0]) if selected_index else None + # Reload configuration from file + with open("config.json", "r") as f: + config = json.load(f) + profiles = config.get("profiles", []) + listbox.delete(0, tk.END) # Clear the listbox + for profile in profiles: + listbox.insert(tk.END, profile['name']) + # Reselect the previously selected profile if it exists + if selected_index is not None and selected_index < len(profiles): + listbox.select_set(selected_index) + update_profile_details(None) + +# Function to open the config file +def open_config(): + subprocess.Popen(["start", "config.json"], shell=True) + +# Load configuration from file +with open("config.json", "r") as f: + config = json.load(f) + +# Get the list of profiles +profiles = config.get("profiles", []) +# Create the main window +root = tk.Tk() +root.title(f"Profile Selection - {program_name} {version}") +root.iconbitmap("icon.ico") +# Set a maximum size for the window +width = 850 +height = 300 +root.minsize(width, height) # Adjust the values as needed +center_window(root, width, height) +# Create a frame for listing profiles +profiles_frame = tk.Frame(root) +profiles_frame.pack(side=tk.LEFT, fill=tk.Y, padx=10, pady=10) +# Create a listbox to display available profiles +listbox = tk.Listbox(profiles_frame, selectmode=tk.SINGLE) +listbox.pack(side=tk.LEFT, fill=tk.Y) + +for profile in profiles: + listbox.insert(tk.END, profile['name']) + +# Create a vertical scrollbar for the listbox +scrollbar = tk.Scrollbar(profiles_frame, orient=tk.VERTICAL) +scrollbar.pack(side=tk.RIGHT, fill=tk.Y) +# Link the listbox and scrollbar +listbox.config(yscrollcommand=scrollbar.set) +scrollbar.config(command=listbox.yview) +# Bind the listbox selection event to update and refresh the profile details +listbox.bind('<>', lambda event: (update_profile_details(event), refresh_profile_details())) +listbox.bind('', run_profile) # Run the selected profile when the Enter key is pressed +# Create a frame for displaying profile details +details_frame = tk.Frame(root) +details_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=10, pady=10) +# Create a frame for the profile name and the Refresh button +profile_name_frame = tk.Frame(details_frame) +profile_name_frame.pack(fill="x", pady=(0, 5)) +# Create the label for the selected profile name +profile_name = tk.Label(profile_name_frame, text="Selected Profile: ", anchor="w") +profile_name.pack(side=tk.LEFT) +# Create a Text widget for displaying profile details with a fixed height and scrollbars +profile_details = Text(details_frame, wrap=tk.WORD, height=10, state=tk.DISABLED, exportselection=False) +profile_details.pack(fill="both", expand=True) +# Create a vertical scrollbar for the profile details Text widget +profile_scrollbar = Scrollbar(profile_details, command=profile_details.yview) +profile_scrollbar.pack(side=tk.RIGHT, fill=tk.Y) +# Link the profile details Text widget and scrollbar +profile_details.config(yscrollcommand=profile_scrollbar.set) + +# Function to exit the application +def on_closing(): + root.destroy() + os._exit(0) + +root.protocol("WM_DELETE_WINDOW", on_closing) + +# Create the "Refresh" button within the profile name frame +refresh_button = tk.Button( + profile_name_frame, + text="Refresh", + command=refresh_profile_details, + padx=5, + pady=0, + fg="white", + bg="grey", + activebackground="#606060", + activeforeground="white", + relief=tk.RAISED, + borderwidth=2, +) +refresh_button.pack(side=tk.RIGHT) + +# Create the "Github" button that redirects to github.com +github_button = tk.Button( + profile_name_frame, + text="Github", + command=lambda: os.startfile(github_link), + padx=5, + pady=0, + fg="white", + bg="lightskyblue4", + activebackground="lightsteelblue4", + activeforeground="white", + relief=tk.RAISED, + borderwidth=2, +) +github_button.pack(side=tk.RIGHT, padx=(0, 5)) + +# Create the "Config" button +config_button = tk.Button( + details_frame, + text="Config", + command=open_config, + padx=30, + pady=10, + fg="white", + bg="grey", + activebackground="#606060", + activeforeground="white", + relief=tk.RAISED, + borderwidth=2, +) +config_button.pack(side=tk.LEFT, fill="x", padx=(0, 2), pady=(10, 0), expand=False) + +# Create the "Run" button +run_button = tk.Button( + details_frame, + text="Run", + command=run_profile, + padx=10, + pady=10, + fg="white", + bg="#08872a", + activebackground="#076921", + activeforeground="white", + relief=tk.RAISED, + borderwidth=2, +) + +run_button.pack(side=tk.LEFT, fill="x", padx=(2, 0), pady=(10, 0), expand=True) +# Configure weights for horizontal stretching +details_frame.pack_propagate(False) +# Start with no profile selected +selected_profile = None + +# Select the first profile by default +if profiles: + selected_profile = profiles[0] + listbox.select_set(0) # Highlight the first item in the list + update_profile_details(None) # Update the profile details + +root.mainloop() + +# Access the selected profile's data after the window closes +if selected_profile: + payload = selected_profile.get('payload') + url = selected_profile.get('url', "") + ssid = selected_profile.get('ssid', "") + check_every_second = selected_profile.get('check_every_second', "") + dialog_geometry = selected_profile.get('dialog_geometry', "") + headers = selected_profile.get('headers', "") + if ssid: + expected_ssid = ssid + expected_ssid_lower = expected_ssid.lower() + else: + ssid = "Ethernet" + expected_ssid = ssid + expected_ssid_lower = expected_ssid.lower() + +# Function to send the request +def send_request(): + session = requests.Session() + requests.packages.urllib3.util.ssl_.DEFAULT_CIPHERS += 'HIGH:!DH:!aNULL' + response = session.post(url, json.dumps(payload), headers=headers, allow_redirects=True, verify=False, timeout=10) + response.raise_for_status() + return response + +# Function to check if internet is available +def is_internet_available(): + try: + # -n is the number of pings + # -l is the size of the packet + # -w is the timeout in seconds + subprocess.run(['ping', '8.8.8.8', '-n', '3', '-l', '32', '-w', '20'], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, creationflags=subprocess.CREATE_NO_WINDOW) + return True + except subprocess.CalledProcessError: + return False + +# Function to get the currently connected SSID using system commands (for Windows) +def get_connected_network(): + try: + startupinfo = subprocess.STARTUPINFO() + startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW + # Check if connected to Ethernet + ethernet_output = subprocess.check_output(["netsh", "interface", "show", "interface"], startupinfo=startupinfo).decode("utf-8") + ethernet_lines = ethernet_output.split("\n") + for line in ethernet_lines: + if "Connected" in line: + if "Ethernet" in line: + return "Ethernet" + # If not connected to Ethernet, check Wi-Fi SSID + wifi_output = subprocess.check_output(["netsh", "wlan", "show", "interfaces"], startupinfo=startupinfo).decode("utf-8") + wifi_lines = wifi_output.split("\n") + wifi_ssid = None + for line in wifi_lines: + if "SSID" in line: + wifi_ssid = line.strip().split(": ")[1] + break + if wifi_ssid is not None: + return wifi_ssid + else: + return False + except subprocess.CalledProcessError: + return False + +# Is connected to wifi +def is_connected_to_wifi(): + try: + startupinfo = subprocess.STARTUPINFO() + startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW + wifi_output = subprocess.check_output(["netsh", "wlan", "show", "interfaces"], startupinfo=startupinfo).decode("utf-8") + wifi_lines = wifi_output.split("\n") + for line in wifi_lines: + if "SSID" in line: + return True + else: + return False + except subprocess.CalledProcessError: + return False + +# Function to exit the application +def exit_application(icon, item): + icon.stop() + os._exit(0) + +# Queue to store log messages +log_messages = deque(maxlen=15) +log_dialog = None +log_text = None +log_dialog_open = False +# Get the dialog geometry from the config file +width = dialog_geometry["width"] +height = dialog_geometry["height"] +x = 0 +y = 0 + +# Function to update the window's position and size variables +def update_window_geometry(event): + global x, y, width, height + x, y = event.x, event.y + width, height = event.width, event.height + +# Function to show the log dialog with the currently selected profile name +def show_log_dialog(): + global log_dialog, log_text, log_dialog_open, successful_logins_label + if not log_dialog_open: + log_dialog_open = True + if log_dialog: + log_dialog.title(f"Log Messages ({selected_profile['name']})") # Update title with profile name + log_dialog.deiconify() + update_log() + else: + log_dialog = tk.Tk() + log_dialog.title(f"Log Messages ({selected_profile['name']})") # Update title with profile name + log_text = tk.Text(log_dialog) + log_frame = tk.Frame(log_dialog) + log_frame.pack(fill=tk.BOTH, expand=True) + log_text = tk.Text(log_frame, wrap=tk.WORD) + log_text.pack(fill=tk.BOTH, expand=True) + log_dialog.minsize(200, 120) + # Create label for successful logins count + successful_logins_label = tk.Label(log_dialog, text=f"Successful Logins: {successful_logins_count}", anchor="w", padx=5) + successful_logins_label.place(x=0, rely=1.0, anchor=tk.SW, bordermode=tk.OUTSIDE) + update_log() + log_dialog.protocol("WM_DELETE_WINDOW", hide_log_dialog) + center_window(log_dialog, width, height) + log_dialog.bind("", update_window_geometry) + log_dialog.mainloop() + else: + hide_log_dialog() + +# Function to hide the log dialog +def hide_log_dialog(): + global log_dialog, log_text, log_dialog_open + log_dialog_open = False + if log_dialog: + log_dialog.withdraw() + log_dialog = None + log_text = None + +# Function to get the last log messages +def get_last_log_messages(): + global log_messages + return list(log_messages) + +# Function to update the log +def update_log(): + if log_text: + log_text.delete(1.0, tk.END) + for message in get_last_log_messages(): + log_text.insert(tk.END, message + "\n") + +# Add a global variable to keep track of successful logins count +successful_logins_count = 0 +successful_logins_label = None + +# Function to add a message to the log +def add_to_log(message): + global log_messages, successful_logins_count + now = datetime.now() + timestamp = now.strftime("%Y-%m-%d %H:%M:%S") + log_messages.append(f"({timestamp}) {message}") + update_log() # Update the log window + +# Function to update the log +def update_log(): + if log_text: + log_text.delete(1.0, tk.END) + for message in get_last_log_messages(): + log_text.insert(tk.END, message + "\n") + update_successful_logins_count() # Update the successful logins count + +# Function to update the successful logins count label +def update_successful_logins_count(): + if log_dialog: + if successful_logins_label: + successful_logins_label.config(text=f"Successful Logins: {successful_logins_count}") + +# Function to add a message to the log file (txt) +def save_to_file(message): + # Get the current date and time + now = datetime.now() + timestamp = now.strftime("%Y-%m-%d %H:%M:%S") + # Format the message with the timestamp + formatted_message = f"({timestamp}) {message}\n" + # Open the file in append mode and write the formatted message + with open("log.txt", "a") as f: + f.write(formatted_message) + +# Create the system tray icon +def create_system_tray_icon(): + image = Image.open("icon.ico") + menu = pystray.Menu( + pystray.MenuItem('Show Log', show_log_dialog, default=True), + pystray.MenuItem('Exit', exit_application) + ) + icon = pystray.Icon("my_icon", image, "HotspotAutoLogin", menu) + icon.run() + +# Function to check network status +running = True +errorcount = 0 +request_success = False +request_errorcount = 0 +sleepcount = check_every_second +connected_ssid_lower = None +def check_network_status(): + global running, errorcount, sleepcount, connected_ssid_lower, ssid, response, request_success, request_errorcount, successful_logins_count + while running and errorcount < 10: + connected_ssid = get_connected_network() + if (connected_ssid): + connected_ssid_lower = connected_ssid.lower() + if (connected_ssid) and ((connected_ssid_lower == expected_ssid_lower)): + if is_connected_to_wifi() and (connected_ssid == "Ethernet" and expected_ssid == "Ethernet"): + message = "You are both connected to Wi-Fi and Ethernet. Disconnecting from Wi-Fi and continuing with Ethernet..." + add_to_log(message) + startupinfo = subprocess.STARTUPINFO() + startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW + subprocess.check_output(['netsh', 'wlan', 'disconnect', 'interface=' + "Wi-Fi"], startupinfo=startupinfo).decode("utf-8") + time.sleep(5) + if (is_internet_available()): + sleepcount = check_every_second + request_success = False + request_errorcount = 0 + message = "Connected to {} and internet connection is available. Checking again in {} seconds...".format(connected_ssid, str(sleepcount)) + errorcount = 0 + add_to_log(message) + elif request_success and request_errorcount < 3: + # If the request was successful but there is still no internet connection, wait for a few seconds and try again + sleepcount = 60 + request_errorcount += 1 + message = "Even the request was successful, there is still no internet connection. There might be a problem with your network. If you are using a VPN, please check your VPN connection. Giving it some time and checking again in {} seconds... (Request Again: {}/3)".format(str(sleepcount), str(request_errorcount)) + add_to_log(message) + save_to_file(message) + else: + sleepcount = 5 + message = "Connected to {} but internet is down. Sending the request in {} seconds...".format(connected_ssid, str(sleepcount)) + add_to_log(message) + save_to_file(message) + time.sleep(sleepcount) + try: + # Function to send request + response = send_request() + if response.ok: + errorcount = 0 + sleepcount = 15 + request_success = True + request_errorcount = 0 + successful_logins_count += 1 + message = "Request was successful. Checking the internet connection in {} seconds...".format(str(sleepcount)) + add_to_log(message) + save_to_file(message) + except requests.exceptions.RequestException as e: + errorcount += 1 + sleepcount = 3 + request_success = False + if isinstance(e, requests.exceptions.HTTPError): + # Handle HTTP errors with different messages for different status codes + status_code = e.response.status_code + if status_code == 400: + message = "Request failed with 400 Bad Request error. Please check your payload and URL. Trying to reconnect to the network... (Errors: {}/10)".format(errorcount) + elif status_code == 404: + message = "Request failed with 404 Not Found error. Trying to reconnect to the network... (Errors: {}/10)".format(errorcount) + elif status_code == 500: + message = "Request failed with 500 Internal Server Error. Trying to reconnect to the network... (Errors: {}/10)".format(errorcount) + elif status_code == 503: + message = "Request failed with 503 Service Unavailable error. Trying to reconnect to the network... (Errors: {}/10)".format(errorcount) + else: + message = "Request failed with HTTP error ({}). Trying to reconnect to the network... (Errors: {}/10)".format(status_code, errorcount) + else: + message = "Request failed with error: {}. If you are usşng a VPN, please disable it for now. Trying to reconnect to the network... (Errors: {}/10)".format(e, errorcount) + add_to_log(message) + save_to_file(message) + time.sleep(sleepcount) + # If the payload URL is not reachable, try to reconnect to the network + if connected_ssid == "Ethernet": + try: + # Disable and enable the Ethernet adapter + message = "Disconnecting from Ethernet..." + add_to_log(message) + subprocess.check_output(['ipconfig', '/release', "Ethernet"], shell=True, text=True) + time.sleep(5) + subprocess.check_output(['ipconfig', '/renew', "Ethernet"], shell=True, text=True) + message = "Connected to Ethernet. Running the script again..." + add_to_log(message) + time.sleep(5) + except Exception as e: + errorcount += 1 + message = "Failed to reconnect to Ethernet: {} (Errors: {}/10)".format(e, errorcount) + add_to_log(message) + save_to_file(message) + else: + try: + # Disconnect and reconnect to the Wi-Fi network + message = "Disconnecting from {}...".format(ssid) + add_to_log(message) + startupinfo = subprocess.STARTUPINFO() + startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW + subprocess.check_output(['netsh', 'wlan', 'disconnect', 'interface=' + "Wi-Fi"], startupinfo=startupinfo).decode("utf-8") + time.sleep(5) + subprocess.check_output(['netsh', 'wlan', 'connect', 'name=' + ssid], startupinfo=startupinfo).decode("utf-8") + message = "Connected to {}. Running the script again...".format(ssid) + add_to_log(message) + time.sleep(8) + except Exception as e: + errorcount += 1 + message = "Failed to reconnect to {}: {} (Errors: {}/10)".format(ssid, e, errorcount) + add_to_log(message) + save_to_file(message) + continue + else: + startupinfo = subprocess.STARTUPINFO() + startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW + wifi_output = subprocess.check_output(["netsh", "interface", "show", "interface"], startupinfo=startupinfo).decode("utf-8") + wifi_lines = wifi_output.split("\n") + wifi_enabled = False + for line in wifi_lines: + if "Wi-Fi" in line: + if "Enabled" in line: + wifi_enabled = True + break + if connected_ssid == "Ethernet": + sleepcount = 30 + message = "If you want to connect to {}, please unplug your ethernet cable or disconnect from {}. Checking again in {} seconds...".format(expected_ssid, connected_ssid, str(sleepcount)) + errorcount = 0 + add_to_log(message) + elif expected_ssid == "Ethernet": + sleepcount = 30 + message = "Looks like your ethernet cable is not plugged in. Please plug in your ethernet cable. Checking again in {} seconds...".format(str(sleepcount)) + errorcount = 0 + add_to_log(message) + elif wifi_enabled: + if (connected_ssid): + sleepcount = 10 + message = "You are connected to {}, but you need to be connected to {}. Trying to connect {}...".format(connected_ssid, expected_ssid, expected_ssid) + add_to_log(message) + else: + sleepcount = 10 + message = "You are not connected to {}. Trying to connect {}...".format(expected_ssid, expected_ssid) + add_to_log(message) + try: + result = subprocess.run(['netsh', 'wlan', 'connect', 'name=' + expected_ssid], capture_output=True, text=True, startupinfo=startupinfo) + if result.returncode != 0: + if result.returncode == 1: + sleepcount = 20 + message = "Could not find a Wi-Fi network with SSID: {}. If your Wi-Fi is disabled, please enable it. Checking again in {} seconds...".format(expected_ssid, str(sleepcount)) + add_to_log(message) + else: + raise subprocess.CalledProcessError(result.returncode, result.args, result.stderr) + else: + message = "Connected to {}. Running the script again in {} seconds...".format(expected_ssid, str(sleepcount)) + add_to_log(message) + except (subprocess.CalledProcessError, ValueError) as e: + errorcount += 1 + sleepcount = 15 + if isinstance(e, subprocess.CalledProcessError): + message = "Failed to connect to {}: {}. Trying again in {} seconds... (Errors:{}/10)".format(expected_ssid, e, str(sleepcount), errorcount) + else: + message = str(e) + ". Trying again in {} seconds... (Errors:{}/10)".format(str(sleepcount), errorcount) + add_to_log(message) + save_to_file(message) + elif wifi_enabled == False: + sleepcount = 30 + errorcount += 1 + message = "You are not connected to Ethernet and your Wi-Fi is disabled. Please enable your Wi-Fi or plug in your ethernet cable. Checking again in {} seconds... (Errors: {}/10)".format(str(sleepcount), errorcount) + add_to_log(message) + save_to_file(message) + else: + sleepcount = 30 + errorcount += 1 + message = "Something went wrong. Please check your Wi-Fi or Ethernet connection. Checking again in {} seconds... (Errors: {}/10)".format(str(sleepcount), errorcount) + save_to_file(message) + add_to_log(message) + time.sleep(sleepcount) # Sleep ... seconds before trying again + message = "Maximum error count reached. Exiting in 5 seconds..." + add_to_log(message) + save_to_file(message) + time.sleep(5) + running = False + os._exit(0) + +if __name__ == '__main__': + # Create a thread for the system tray icon + tray_thread = threading.Thread(target=create_system_tray_icon) + tray_thread.start() + # Open Log Messages at startup + open_log_messages_startup = threading.Thread(target=show_log_dialog) + open_log_messages_startup.start() + # Start the network status checking in the main thread + check_network_status() + # Wait for all threads to finish + tray_thread.join() \ No newline at end of file diff --git a/main/config.json b/main/config.json new file mode 100644 index 0000000..5d56751 --- /dev/null +++ b/main/config.json @@ -0,0 +1,38 @@ +{ + "profiles": [ + { + "name": "My School Wi-Fi", + "ssid": "SCHOOL_WIFI_5G", + "url": "https://connect.schoolwifi.com/api/portal/dynamic/authenticate", + "payload": { + "username": "85795013@myschool.com", + "password": "123455678" + }, + "headers": { + "Content-Type": "application/json;charset=UTF-8", + "Connection": "keep-alive", + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3" + }, + "check_every_second": 600, + "dialog_geometry": { + "width": 1024, + "height": 500 + } + }, + { + "name": "My School ETHERNET", + "url": "http://10.3.41.15:8002/index.php?zone=dormnet", + "payload": "&auth_user=85795013%40myschool.com&auth_pass=123455678&redirurl=&accept=Login", + "headers": { + "Content-Type": "application/x-www-form-urlencoded", + "Connection": "keep-alive", + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36 Edg/118.0.2088.69" + }, + "check_every_second": 600, + "dialog_geometry": { + "width": 1024, + "height": 500 + } + } + ] +} \ No newline at end of file diff --git a/main/icon.ico b/main/icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..51836945b5eb5b6ff922dc051473ae06e067abea GIT binary patch literal 3782 zcmeHKO-NKx6h7%>KTauBt|qfpMYS@*O1*C)!G#orZ^lZ9HZFo_Q4mz%ltDk+7Sgh9 z5wxgC(z$P>poL|S(V~q~8=+*Tq%tON-?{Vd@ETv^8+%KyoVk~C&-uP{&pr3dNVEcf zjg5rQCVIG@sDX&KL!hEo2e$cAn@C#+On&*C%*i$jxKR(R4>}B|=5^Y4vtQB? zZ%m#`XD>I$WA-b_^Wo?F*TJuNmw1bP%nR&m2G4J1ZqC0gMXTB8-on0%4Lj4em2Eno zbglx)=;+#)eSLr)~e~$Ya`<5Fzw^Ga@#?$sayV}ndG1x)-2J+qQhvC(E@6*lE zTg+#BM&dhrhdOGz#=6$p6Lt;zxZMBlAE>1za{BgN&iN53>Y1B!?%ww}^s`#U3ygUZetMjf4 zHKe~+E|J>@-98Zf?9xixnS*;ZE-=7(S;vlkDPoIE21YN)Poa_tzgTL;mgu9fpXv?@#+ze6^CO%zTj&%!h{3zlSzXS1I z>~i;wHLt|_v9C?hUgXi+MuBw>XJ}z;_G8gJ&;jE-+u;29a#+aIBlhDsc?RB{a`qwZ z4RXDj+?W|X_c-GI5)y~!y^GH{SC8TGoAqx(K0A8jfAbeG1}php6~=daq#9(yyp02==ngat6ZAfKrn& zrJSfzsfbcUT7K7%np;CEM=3Q$mdZeX7gCps%##((Q&a3GZOPA4_3#u`#T+LZO^K}T YLn)%jBvCAje?{{|c}3NErPKoX0~@U5TmS$7 literal 0 HcmV?d00001 diff --git a/main/last_known_directory.txt b/main/last_known_directory.txt new file mode 100644 index 0000000..e4ce49d --- /dev/null +++ b/main/last_known_directory.txt @@ -0,0 +1 @@ +C:\Users\deniz\Desktop\t diff --git a/main/requirements.txt b/main/requirements.txt new file mode 100644 index 0000000..f1abb63 --- /dev/null +++ b/main/requirements.txt @@ -0,0 +1,4 @@ +urllib3==1.26.18 +requests==2.31.0 +pystray==0.19.5 +dnspython==2.6.1 \ No newline at end of file diff --git a/run.bat b/run.bat index 5939a25..a13f752 100644 --- a/run.bat +++ b/run.bat @@ -1,52 +1,118 @@ @echo off setlocal enabledelayedexpansion +cd /d "%~dp0" set NAME=HotspotAutoLogin -set RUN=HotspotAutoLogin.pyw -set "requirementsFile=requirements.txt" +set PROJECTFOLDER=main +set RUN=%PROJECTFOLDER%\HotspotAutoLogin.pyw +set requirementsFile=%PROJECTFOLDER%\requirements.txt set VENV_PATH=.venv set ACTIVATE_PATH=%VENV_PATH%\Scripts\activate +set CURRENT_DIR=%CD% +set LAST_DIR_FILE=%PROJECTFOLDER%\last_known_directory.txt +set refrenv=%PROJECTFOLDER%\refrenv.bat +set PYTHON_DOWNLOAD_URL=https://www.python.org/ftp/python/3.12.2/python-3.12.2-amd64.exe + +:: Display provided argument if any +if not "%~1"=="" ( + echo Open with: "%~1" +) :: Check if Python is installed -pip --version >nul 2>&1 -if errorlevel 1 ( - echo Python is not installed. - set /p "userinp=Do you want to install Python (y/n)? " - if /i "!userinp!"=="y" ( - echo Opening download link... - start https://www.python.org/downloads/ - exit /b +pip --version >nul 2>&1 && (set python_installed=true) || (set python_installed=false) +if not "%python_installed%"=="true" ( + echo Python is required to run this program. + set /p "userinp=Do you want to install Python (y/n)?: " + if /i "!userinp!"=="y" ( + echo Downloading Python... + winget install python --accept-package-agreements + if errorlevel 1 ( + echo Download failed using winget method. Trying to download manually... + :bothwingetfailed + if not exist "%PROJECTFOLDER%\python_installer.exe" ( + echo Downloading Python from %PYTHON_DOWNLOAD_URL% + curl %PYTHON_DOWNLOAD_URL% -o %PROJECTFOLDER%\python_installer.exe + if not exist "%PROJECTFOLDER%\python_installer.exe" ( + echo Download failed using curl. Trying with the powershell method + powershell -Command "& {Invoke-WebRequest -Uri '%PYTHON_DOWNLOAD_URL%' -OutFile '%PROJECTFOLDER%\python_installer.exe'}" + if errorlevel 1 ( + echo Failed to install Python. Please install Python manually and try again. + pause + exit /b + ) + ) + ) + echo Installing Python. Please wait... + start /wait %PROJECTFOLDER%\python_installer.exe /quiet InstallAllUsers=1 PrependPath=1 Include_test=0 + if errorlevel 1 ( + echo Failed to install Python. Please install Python manually and try again. + pause + exit /b + ) + del %PROJECTFOLDER%\python_installer.exe + goto python_ok + ) ) else ( - echo Please install Python and try again. - pause - exit /b + echo Python is required to run this program. Please install Python and try again. + pause + exit /b ) + :python_ok + echo Python installed successfully. + :: Refresh the environment variables + call %refrenv% + set python_installed=true ) :: Check if virtual environment exists IF NOT EXIST %VENV_PATH% ( - echo Creating virtual environment... - python -m venv %VENV_PATH% + echo Creating virtual environment... + echo %CURRENT_DIR% > "%LAST_DIR_FILE%" + python -m venv %VENV_PATH% ) ELSE ( - goto check_python + goto check_python ) :: Check if Python exists at the path stored in the virtual environment -set counut_python=0 +set count_python=0 :check_python %VENV_PATH%\Scripts\python.exe --version >nul 2>&1 if %errorlevel% neq 0 ( - set /a count_python+=1 - echo Python not found at the path stored in the virtual environment. - echo Recreating virtual environment... - rmdir /s /q %VENV_PATH% - python -m venv %VENV_PATH% - if !count_python! lss 3 ( - goto check_python - ) else ( - echo Something went wrong. Please try again. - pause - exit /b - ) + set /a count_python+=1 + echo Python not found at the path stored in the virtual environment. + echo Recreating virtual environment... + rmdir /s /q %VENV_PATH% + python -m venv %VENV_PATH% + if !count_python! lss 3 ( + goto check_python + ) else ( + echo Something went wrong. Please try again. + pause + exit /b + ) +) + +:: Check if the current directory has changed since the last run +if exist "%LAST_DIR_FILE%" ( + set /p last_dir=<"%LAST_DIR_FILE%" + cd "%CURRENT_DIR%" && ( + if not exist "!last_dir!" ( + echo Current directory: %CURRENT_DIR% + echo Last known directory: !last_dir! + echo Looks like the directory of the program has been changed. + echo It is hihgly recommended to recreate the virtual environment. + set /p "recreate_venv=Recreate the virtual environment (y/n)?: " + if /i "!recreate_venv!"=="y" ( + echo Recreating virtual environment... + rmdir /s /q %VENV_PATH% + python -m venv %VENV_PATH% + :: Update the last directory file + echo %CURRENT_DIR% > "%LAST_DIR_FILE%" + ) + ) + ) +) else ( + :: Create the last directory file if it doesn't exist + echo %CURRENT_DIR% > "%LAST_DIR_FILE%" ) :: Activate the virtual environment @@ -55,45 +121,46 @@ set count_activate=0 echo Activating virtual environment... call %ACTIVATE_PATH% if errorlevel 1 ( - set /a count_activate+=1 - echo Failed to activate virtual environment. - echo Recreating virtual environment... - rmdir /s /q %VENV_PATH% - python -m venv %VENV_PATH% - if !count_activate! lss 3 ( - goto activate - ) else ( - echo Failed to activate virtual environment. Please try again. - pause - exit /b - ) + set /a count_activate+=1 + echo Failed to activate virtual environment. + echo Recreating virtual environment... + rmdir /s /q %VENV_PATH% + python -m venv %VENV_PATH% + if !count_activate! lss 3 ( + goto activate + ) else ( + echo Failed to activate virtual environment. Please try again. + pause + exit /b + ) ) :: Get the list of installed packages using pip freeze echo Checking the requirements... -for /f "tokens=1,* delims==" %%i in ('pip freeze') do ( - set installed[%%i]=%%j +for /f "tokens=1,* delims==" %%i in ('pip freeze --all') do ( + set installed[%%i]=%%j ) :: Compare with the requirements from the requirements.txt file for /f "tokens=1,* delims==" %%i in (%requirementsFile%) do ( - if not "!installed[%%i]!"=="%%j" ( - echo Installing package: %%i==%%j - pip install %%i==%%j --upgrade --quiet - if errorlevel 1 ( - echo Failed to install %%i==%%j. Please check your internet connection and try again. - pause - exit /b - ) - ) + if not "!installed[%%i]!"=="%%j" ( + echo Installing package: %%i==%%j + pip install %%i==%%j --upgrade --quiet + if errorlevel 1 ( + echo Failed to install %%i==%%j. Please check your internet connection and try again. + pause + exit /b + ) + ) ) :: Run the program echo Starting %NAME%... -start /B "" "%VENV_PATH%/Scripts/pythonw.exe" %RUN% +start /B "" "%VENV_PATH%\Scripts\pythonw.exe" %RUN% %* > nul 2>&1 if errorlevel 1 ( - echo Failed to start %NAME%. Please try again. - pause - exit /b + echo Failed to start %NAME%. Please try again. + pause + exit /b ) + exit /b \ No newline at end of file