mirror of
https://github.com/Stone-Red-Code/HotspotAutoLogin.git
synced 2026-11-02 07:18:02 +01:00
Compare commits
43
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
95e660bfb2 | ||
|
|
3051663c22 | ||
|
|
5a81ba91df | ||
|
|
7c56667d44 | ||
|
|
880b03293e | ||
|
|
4c4a477533 | ||
|
|
5f250b99cd | ||
|
|
b6eb5d680f | ||
|
|
0dc0eb5883 | ||
|
|
b3afe9304f | ||
|
|
38f88a0ab1 | ||
|
|
93731a0c31 | ||
|
|
71d92441a4 | ||
|
|
b402141d5e | ||
|
|
574dd5ee0c | ||
|
|
8e64ad3aa2 | ||
|
|
a835545c90 | ||
|
|
fc35b850f0 | ||
|
|
af9314abb8 | ||
|
|
5fe32a52ab | ||
|
|
3ef3880ae4 | ||
|
|
f81b870200 | ||
|
|
be80f99b70 | ||
|
|
db97b696d8 | ||
|
|
5c512632a1 | ||
|
|
e2e8bec629 | ||
|
|
9efb27e0f1 | ||
|
|
f1c9ebc6a5 | ||
|
|
3a1424679a | ||
|
|
8210a16df3 | ||
|
|
22a5d44c96 | ||
|
|
76301bdbb9 | ||
|
|
ee73fcdc78 | ||
|
|
4cc9cbe92a | ||
|
|
c87599b544 | ||
|
|
26273ca509 | ||
|
|
4a2e6e2278 | ||
|
|
7e988d816b | ||
|
|
6a854b2581 | ||
|
|
7c9ccb39e5 | ||
|
|
bd0ab39771 | ||
|
|
2b64fd69bb | ||
|
|
c08620677f |
+369
-113
@@ -6,38 +6,243 @@ 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)
|
||||
requests.packages.urllib3.util.ssl_.DEFAULT_CIPHERS += 'HIGH:!DH:!aNULL'
|
||||
|
||||
try:
|
||||
requests.packages.urllib3.contrib.pyopenssl.DEFAULT_SSL_CIPHER_LIST += 'HIGH:!DH:!aNULL'
|
||||
except AttributeError:
|
||||
# no pyopenssl support used / needed / available
|
||||
pass
|
||||
# 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():
|
||||
os.system("config.json")
|
||||
|
||||
# Load configuration from file
|
||||
with open("config.json", "r") as f:
|
||||
config = json.load(f)
|
||||
|
||||
# Your authentication payload and URL
|
||||
payload = config["payload"]
|
||||
url = config["url"]
|
||||
# Get the list of profiles
|
||||
profiles = config.get("profiles", [])
|
||||
|
||||
# Convert the expected SSID to lowercase (or uppercase)
|
||||
expected_ssid = config["ssid"].lower()
|
||||
# Create the main window
|
||||
root = tk.Tk()
|
||||
root.title("Profile Selection")
|
||||
|
||||
# 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('<<ListboxSelect>>', lambda event: (update_profile_details(event), refresh_profile_details()))
|
||||
listbox.bind('<Return>', 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 "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 = None
|
||||
|
||||
# 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=3)
|
||||
response.raise_for_status() # Check for HTTP errors
|
||||
return response
|
||||
|
||||
# Function to check if internet is available
|
||||
def is_internet_available():
|
||||
try:
|
||||
# Try to send a simple HTTP GET request to a known website
|
||||
response = requests.get("https://www.google.com")
|
||||
# If the request was successful, it means the internet is available
|
||||
return response.status_code == 200
|
||||
except requests.ConnectionError:
|
||||
# If an exception is raised, there's no internet connectivity
|
||||
# -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', '3'], 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)
|
||||
@@ -45,7 +250,6 @@ def get_connected_network():
|
||||
try:
|
||||
startupinfo = subprocess.STARTUPINFO()
|
||||
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
|
||||
|
||||
# Get Wi-Fi SSID
|
||||
wifi_output = subprocess.check_output(["netsh", "wlan", "show", "interfaces"], startupinfo=startupinfo).decode("utf-8")
|
||||
wifi_lines = wifi_output.split("\n")
|
||||
@@ -54,7 +258,6 @@ def get_connected_network():
|
||||
if "SSID" in line:
|
||||
wifi_ssid = line.strip().split(": ")[1]
|
||||
break
|
||||
|
||||
if wifi_ssid is not None:
|
||||
return wifi_ssid
|
||||
elif wifi_ssid is None:
|
||||
@@ -64,50 +267,73 @@ def get_connected_network():
|
||||
for line in ethernet_lines:
|
||||
if "Connected" in line:
|
||||
if "Ethernet" in line:
|
||||
return "Ethernet Network"
|
||||
return "Ethernet"
|
||||
else:
|
||||
return None
|
||||
return False
|
||||
except subprocess.CalledProcessError:
|
||||
return None
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
# 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)
|
||||
|
||||
# Flag to control the main loop
|
||||
running = True
|
||||
|
||||
# Function to gracefully exit the application
|
||||
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=20)
|
||||
log_messages = deque(maxlen=30)
|
||||
log_dialog = None
|
||||
log_text = None
|
||||
log_dialog_open = False
|
||||
|
||||
# Function to exit the application
|
||||
def exit_application(icon, item):
|
||||
# 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
|
||||
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)
|
||||
update_log()
|
||||
log_dialog.protocol("WM_DELETE_WINDOW", hide_log_dialog)
|
||||
center_window(log_dialog, width, height)
|
||||
log_dialog.bind("<Configure>", 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.destroy()
|
||||
icon.stop()
|
||||
os._exit(0)
|
||||
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():
|
||||
@@ -124,97 +350,121 @@ def add_to_log(message):
|
||||
log_messages.append(f"({timestamp}) {message}")
|
||||
update_log()
|
||||
|
||||
# Function to get the last log messages
|
||||
def get_last_log_messages():
|
||||
global log_messages
|
||||
return list(log_messages)
|
||||
|
||||
# Function to show the log dialog
|
||||
def show_log_dialog(icon, item):
|
||||
global log_dialog, log_text
|
||||
if log_dialog:
|
||||
log_dialog.deiconify()
|
||||
update_log()
|
||||
else:
|
||||
log_dialog = tk.Tk()
|
||||
log_dialog.title("Log Messages")
|
||||
log_text = tk.Text(log_dialog)
|
||||
log_dialog.geometry("1024x500")
|
||||
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)
|
||||
update_log()
|
||||
log_dialog.protocol("WM_DELETE_WINDOW", hide_log_dialog)
|
||||
log_dialog.mainloop()
|
||||
|
||||
# Function to hide the log dialog
|
||||
def hide_log_dialog():
|
||||
global log_dialog
|
||||
if log_dialog:
|
||||
log_dialog.withdraw()
|
||||
# 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.png")
|
||||
|
||||
image = Image.open("icon.ico")
|
||||
menu = pystray.Menu(
|
||||
pystray.MenuItem('Show Log', show_log_dialog),
|
||||
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
|
||||
sleepcount = check_every_second
|
||||
connected_ssid_lower = None
|
||||
def check_network_status():
|
||||
errorcount = 0
|
||||
sleepcount = config["check_every_second"]
|
||||
global running, errorcount, sleepcount, connected_ssid_lower, ssid, response
|
||||
while running and errorcount < 10:
|
||||
connected_ssid = get_connected_network()
|
||||
if connected_ssid.lower() == expected_ssid or connected_ssid == "Ethernet Network":
|
||||
if is_internet_available():
|
||||
if (connected_ssid):
|
||||
connected_ssid_lower = connected_ssid.lower()
|
||||
if (connected_ssid) and ((connected_ssid_lower == expected_ssid_lower) or (connected_ssid == "Ethernet")):
|
||||
if (is_internet_available()):
|
||||
sleepcount = check_every_second
|
||||
message = "Connected to {} and internet connection is available. Checking again in {} seconds...".format(connected_ssid, str(sleepcount))
|
||||
errorcount = 0
|
||||
sleepcount = config["check_every_second"]
|
||||
message = "Connected to " + config["ssid"] + " and internet connection is available. Checking again in " + str(sleepcount) + " seconds..."
|
||||
add_to_log(message)
|
||||
else:
|
||||
message = "Connected to " + config["ssid"] + ", but internet connection is down. Running the script..."
|
||||
sleepcount = 1
|
||||
message = "Connected to {} but internet is down. Sending the request...".format(connected_ssid)
|
||||
add_to_log(message)
|
||||
save_to_file(message)
|
||||
|
||||
time.sleep(sleepcount)
|
||||
# Send the request and handle potential errors
|
||||
try:
|
||||
response = requests.post(url, data=json.dumps(payload), headers=headers, verify=False, timeout=10, allow_redirects=True)
|
||||
|
||||
# Check the response status code
|
||||
if response.status_code == 200:
|
||||
# Success!
|
||||
try:
|
||||
# Function to send request
|
||||
response = send_request()
|
||||
if response.ok:
|
||||
errorcount = 0
|
||||
sleepcount = config["check_every_second"]
|
||||
message = "Request was successful. Connection established! Checking again in " + str(sleepcount) + " seconds..."
|
||||
add_to_log(message)
|
||||
save_to_file(message)
|
||||
else:
|
||||
errorcount += 1
|
||||
sleepcount = 10 # Sleep ... seconds before trying again
|
||||
message = "Request failed with status code: {}. Trying again in {} seconds... (Errors: {}/10)".format(response.status_code, sleepcount, errorcount)
|
||||
sleepcount = 20
|
||||
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:
|
||||
# Catch the exception and add a delay before trying again
|
||||
errorcount += 1
|
||||
sleepcount = 10
|
||||
message = "Failed to send request: {}. Trying again in {} seconds... (Errors: {}/10)".format(e, sleepcount, errorcount)
|
||||
sleepcount = 1
|
||||
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:{}. Trying to reconnect to the network... (Errors: {}/10)".format(e, errorcount)
|
||||
# Print the response message
|
||||
add_to_log(message)
|
||||
save_to_file(message)
|
||||
time.sleep(sleepcount)
|
||||
# If the payload URL is not reachable, try to reconnect to the network
|
||||
startupinfo = subprocess.STARTUPINFO()
|
||||
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
|
||||
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)
|
||||
time.sleep(2)
|
||||
message = "Connected to Ethernet. Running the script again..."
|
||||
add_to_log(message)
|
||||
except Exception as e:
|
||||
message += "\nFailed to reconnect to Ethernet: {}".format(e)
|
||||
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)
|
||||
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")
|
||||
time.sleep(2)
|
||||
message = "Connected to {}. Running the script again...".format(ssid)
|
||||
add_to_log(message)
|
||||
except Exception as e:
|
||||
message += "\nFailed to reconnect to {}: {}".format(ssid, e)
|
||||
add_to_log(message)
|
||||
save_to_file(message)
|
||||
continue
|
||||
else:
|
||||
sleepcount = 10 # Sleep ... seconds before trying again
|
||||
message = "Not connected to {}. Checking again in {} seconds...".format(config["ssid"], sleepcount)
|
||||
message = "Not connected to {} or any other network. Checking again in {} seconds...".format(expected_ssid, str(sleepcount))
|
||||
add_to_log(message)
|
||||
save_to_file(message)
|
||||
time.sleep(sleepcount)
|
||||
|
||||
time.sleep(sleepcount) # Sleep ... seconds before trying again
|
||||
|
||||
@@ -222,14 +472,20 @@ def check_network_status():
|
||||
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.daemon = True # Set as a daemon thread so it exits when the main program exits
|
||||
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()
|
||||
@@ -1,35 +1,33 @@
|
||||
# HotspotAutoLogin Wi-Fi/Hotspot Autologin Script (Automate WEB Logins)
|
||||
# HotspotAutoLogin: Automatic Wi-Fi/Ethernet WEB Logins (Automate WEB/Captive Portal Logins)
|
||||
HotspotAutoLogin is a script that designed to automate the login process for Wi-Fi or Ethernet networks that require web-based authentication. This script is intended for situations where you often connect to networks that require a web login, such as public hotspots in cafes, hotels, or airports. The script continuously monitors the internet connection and automatically logs you in when necessary.
|
||||
|
||||
<img title="Profile Selection" src='examples/Profiles.png' width='100%'>
|
||||
|
||||

|
||||
|
||||
HotspotAutoLogin is a script that designed to automate the login process for Wi-Fi or hotspot networks that require web-based authentication. This script is intended for situations where you often connect to networks that require a web login, such as public hotspots in cafes, hotels, or airports. The script continuously monitors your network connection and automatically logs you in when necessary.
|
||||
|
||||
> # [Download the Latest Executable (.exe) Release](https://github.com/denizsafak/HotspotAutoLogin/releases/latest)
|
||||
> You can download the executable (.exe) version of the same script, making it easy to use without the need to install Python or other libraries.
|
||||
|
||||
## `How It Works`
|
||||
### Network Monitoring
|
||||
The script will continuously monitor your network connection. When you connect to the specified SSID but there is no internet access, it will attempt to log in automatically.
|
||||
## `How to Run?`
|
||||
|
||||
### Autologin
|
||||
If you are connected to the correct SSID but lack internet access, the script attempts to log in by sending an POST request to the provided URL with the specified credentials. If the login is successful (status code 200), the script will continue monitoring.
|
||||
### Option 1: Executable Script
|
||||
- If you don't want to install Python, you can download the precompiled executable version from the Releases section.
|
||||
[Download the Latest Executable (.exe) Release](https://github.com/denizsafak/HotspotAutoLogin/releases/latest)
|
||||
- Double-click on HotspotAutoLogin.exe to launch the application. The wallpaper information display will appear on your screen.
|
||||
|
||||
### Logging
|
||||
Any important events or actions taken by the script are logged both in a log file (log.txt) and in the log window that can be accessed via the system tray icon.
|
||||
### Option 2: Run with Python
|
||||
- Clone or download the repository to your local machine.
|
||||
- Install Python to your computer.
|
||||
- Run "run.bat" file.
|
||||
|
||||
### Configuration
|
||||
The script reads its configuration from a config.json file. This file contains the necessary information, such as your login credentials, the URL of the authentication portal, the SSID (network name) to which you want to connect, and the frequency of network checks in seconds.
|
||||
## `Features`
|
||||
- The script will continuously monitor the internet connection. When you connect to the specified SSID but there is no internet access, it will attempt to log in automatically.
|
||||
- If you are connected to the correct SSID but lack internet access, the script attempts to log in by sending an POST request to the provided URL with the specified credentials. If the login is successful, the script will continue monitoring.
|
||||
- Any important events or actions taken by the script are logged both in a log file (log.txt) and in the log window that can be accessed via the system tray icon.
|
||||
- The script reads its configuration from a config.json file. This file contains the necessary information, such as your login credentials, the URL of the authentication portal, the SSID (network name) to which you want to connect, and the frequency of network checks in seconds.
|
||||
|
||||
## `Usage`
|
||||
|
||||
> [!IMPORTANT]
|
||||
> ### Python Requirements
|
||||
> If you are running it **directly with Python**, you need to install all the libraries that's used in the script and you need to install this specific library:
|
||||
> ```
|
||||
> python -m pip install requests "urllib3<2"
|
||||
> ```
|
||||
|
||||
|
||||
### Configuration
|
||||
Edit the config.json file with your specific information, including your login credentials, the portal URL, the SSID of the network you want to connect to, and the check interval in seconds. [Click to learn how to Configure the config.json](#how-to-configure-the-configjson)
|
||||
|
||||
@@ -54,15 +52,54 @@ In the "Payload" tab, you can find your form data or request payload that displa
|
||||

|
||||
|
||||
`"ssid":` This is your network's SSID. For example "MyHome_5G"
|
||||
> [!IMPORTANT]
|
||||
> **If you are using a wired (ethernet) connection, you don't need to enter ssid. You can leave it blank.**
|
||||
|
||||
`"check_every_second":` The frequency of the script for checking your internet connection status. For example "100", it will try to access the internet every 100 seconds.
|
||||
|
||||
> ## Example useage:
|
||||
>
|
||||

|
||||
|
||||
## Dependencies
|
||||
The script uses various Python libraries, including requests for making POST requests, pystray for creating the system tray icon, and tkinter for the log window. Make sure you have all the required libraries.
|
||||
```json
|
||||
{
|
||||
"profiles": [
|
||||
{
|
||||
"name": "My School Wi-Fi",
|
||||
"ssid": "SCHOOL_WIFI_5G",
|
||||
"url": "https://connect.schoolwifi.com/api/portal/dynamic/authenticate",
|
||||
"payload": {
|
||||
"username": "[email protected]",
|
||||
"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
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> - The script relies on the assumption that the web portal uses basic authentication. If the portal uses a more complex login mechanism, additional adjustments may be necessary.
|
||||
@@ -70,3 +107,5 @@ The script uses various Python libraries, including requests for making POST req
|
||||
|
||||
## Disclaimer
|
||||
> Use this script responsibly and only on networks where you have the right to access and use the provided services. Unauthorized access to networks is illegal and unethical.
|
||||
|
||||
> Tags: Auto Connect WiFi, Wifi Login Wizard, Wifi Auto Login, Auto Hotspot Login, Hotspot Auto Connect, Hotspot Automator, Auto Hotspot Sign In, Wifi Auto Sign In, Web Login Automator, WiFi Login Automator, Hotspot Login Automator, WEB Portal Auto Login, Quick Hotspot Connect
|
||||
|
||||
+36
-7
@@ -1,9 +1,38 @@
|
||||
{
|
||||
"payload": {
|
||||
"username": "PLEASE WRITE YOUR OWN PAYLOAD VALUES HERE. THIS IS AN EXAMPLE",
|
||||
"password": "PLEASE WRITE YOUR OWN PAYLOAD VALUES HERE. THIS IS AN EXAMPLE"
|
||||
},
|
||||
"url": "https://example.com/api/portal/dynamic/authenticate",
|
||||
"ssid": "WRITE YOUR SSID HERE",
|
||||
"check_every_second": 120
|
||||
"profiles": [
|
||||
{
|
||||
"name": "NAME_OF_YOUR_PROFILE (WI-FI EXAMPLE)",
|
||||
"ssid": "SSID_OF_YOUR_WIFI",
|
||||
"url": "PAYLOAD_URL_HERE",
|
||||
"payload": {
|
||||
"username": "PLEASE WRITE YOUR OWN PAYLOAD VALUES HERE. THIS IS AN EXAMPLE",
|
||||
"password": "PLEASE WRITE YOUR OWN PAYLOAD VALUES HERE. THIS IS AN EXAMPLE"
|
||||
},
|
||||
"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": "NAME_OF_YOUR_PROFILE (ETHERNET EXAMPLE)",
|
||||
"url": "PAYLOAD_URL_HERE",
|
||||
"payload": "&auth_user=XXXX%40gmail.com&auth_pass=xxx&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
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 48 KiB |
@@ -0,0 +1,4 @@
|
||||
urllib3==1.26.18
|
||||
requests==2.31.0
|
||||
pystray==0.19.5
|
||||
dnspython==2.6.1
|
||||
@@ -0,0 +1,26 @@
|
||||
@echo off
|
||||
set NAME=HotspotAutoLogin
|
||||
set RUN=HotspotAutoLogin.pyw
|
||||
set "requirementsFile=requirements.txt"
|
||||
set VENV_PATH=.venv
|
||||
set ACTIVATE_PATH=%VENV_PATH%\Scripts\activate
|
||||
|
||||
IF NOT EXIST %ACTIVATE_PATH% (
|
||||
echo Creating virtual environment...
|
||||
virtualenv %VENV_PATH%
|
||||
)
|
||||
|
||||
echo Activating virtual environment...
|
||||
call %ACTIVATE_PATH%
|
||||
|
||||
echo Checking the requirements...
|
||||
for /f "delims=" %%i in (%requirementsFile%) do (
|
||||
pip freeze | findstr /c:%%i > nul
|
||||
if errorlevel 1 (
|
||||
echo Installing package: %%i
|
||||
pip install %%i --upgrade --quiet
|
||||
)
|
||||
)
|
||||
|
||||
echo Starting %NAME%...
|
||||
start /B "" "%VENV_PATH%/Scripts/pythonw.exe" %RUN%
|
||||
Reference in New Issue
Block a user