Compare commits

...
Author SHA1 Message Date
Deniz Şafak 95e660bfb2 Improvements on the UI and some other parts 2024-03-12 22:18:37 +03:00
Deniz Şafak 3051663c22 Update README.md 2024-03-12 16:32:44 +03:00
Deniz Şafak 5a81ba91df Update README.md 2024-03-12 16:29:04 +03:00
Deniz Şafak 7c56667d44 Update README.md 2024-03-12 16:26:09 +03:00
Deniz Şafak 880b03293e Update README.md 2024-03-12 16:21:58 +03:00
Deniz Şafak 4c4a477533 Update README.md 2024-03-12 16:19:18 +03:00
Deniz Şafak 5f250b99cd Update README.md 2024-03-12 16:14:16 +03:00
Deniz Şafak b6eb5d680f Bug fixes, improvements, DNS fix 2024-03-12 04:30:58 +03:00
Deniz Şafak 0dc0eb5883 Delete icon.png 2024-03-12 04:16:59 +03:00
Deniz Şafak b3afe9304f Add files via upload 2024-03-12 04:16:46 +03:00
Deniz Şafak 38f88a0ab1 Bug fixes, improvements, DNS fix 2024-03-12 03:55:25 +03:00
Deniz Şafak 93731a0c31 Update README.md 2024-03-11 02:52:39 +03:00
Deniz Şafak 71d92441a4 Update README.md 2024-03-10 16:40:47 +03:00
Deniz Şafak b402141d5e Update README.md 2024-03-10 16:32:06 +03:00
Deniz Şafak 574dd5ee0c Added virtual env. support 2024-03-10 03:51:50 +03:00
Deniz Şafak 8e64ad3aa2 Added virtual env. support 2024-03-10 03:47:17 +03:00
Deniz Şafak a835545c90 Added virtual env. support 2024-03-10 03:42:21 +03:00
Deniz Şafak fc35b850f0 Add files via upload 2024-03-09 20:42:02 +03:00
Deniz Şafak af9314abb8 Update README.md 2024-03-09 20:20:25 +03:00
Deniz Şafak 5fe32a52ab Added vitrual env. support 2024-03-09 20:15:12 +03:00
Deniz Şafak 3ef3880ae4 Delete EasyWallpaperInfo.pyw 2023-12-12 12:20:54 +03:00
Deniz Şafak f81b870200 Delete NextBackground.exe 2023-12-12 12:20:51 +03:00
Deniz Şafak be80f99b70 Delete NextBackground.ahk 2023-12-12 12:20:40 +03:00
Deniz Şafak db97b696d8 Add files via upload 2023-12-12 12:15:31 +03:00
Deniz Şafak 5c512632a1 Update README.md 2023-11-07 00:55:23 +03:00
Deniz Şafak e2e8bec629 Update README.md 2023-11-07 00:54:10 +03:00
Deniz Şafak 9efb27e0f1 Update README.md 2023-11-05 01:48:31 +03:00
Deniz Şafak f1c9ebc6a5 Update README.md 2023-11-01 13:33:44 +03:00
Deniz Şafak 3a1424679a Update README.md 2023-11-01 13:20:46 +03:00
Deniz Şafak 8210a16df3 Update README.md 2023-11-01 13:07:00 +03:00
Deniz Şafak 22a5d44c96 Add files via upload 2023-11-01 13:00:24 +03:00
Deniz Şafak 76301bdbb9 Update config.json 2023-11-01 12:50:04 +03:00
Deniz Şafak ee73fcdc78 Added profile system and fixed some bugs, improvements. 2023-11-01 12:45:49 +03:00
Deniz Şafak 4cc9cbe92a Bug fixes 2023-10-25 13:16:30 +03:00
Deniz Şafak c87599b544 Bug fixes, revisions 2023-10-18 14:57:48 +03:00
Deniz Şafak 26273ca509 Bug fixes, revisions 2023-10-18 14:56:21 +03:00
Deniz Şafak 4a2e6e2278 Bug fixa 2023-10-18 14:46:05 +03:00
Deniz Şafak 7e988d816b Update README.md 2023-10-17 02:56:28 +03:00
8 changed files with 484 additions and 172 deletions
+357 -141
View File
@@ -1,17 +1,3 @@
import logging, sys, traceback
# Log all errors to a log.txt file
def log_exception(exctype, value, tb):
logging.exception("Uncaught exception: {0}".format(str(value)))
logging.exception("Traceback: {0}".format(''.join(traceback.format_tb(tb))))
logging.shutdown()
sys.exit(1)
sys.excepthook = log_exception
# Log all messages except DEBUG to a log.txt file
logging.basicConfig(filename='log.txt', level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
import json
import os
import requests
@@ -20,34 +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)
# 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 payload
payload = config["payload"]
url = config["url"]
if (config["ssid"]):
expected_ssid = config["ssid"]
expected_ssid_lower = config["ssid"].lower()
else:
config["ssid"] = "Wi-Fi"
expected_ssid = config["ssid"]
expected_ssid_lower = None
# Get the list of profiles
profiles = config.get("profiles", [])
# 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", timeout=10)
# If the request was successful, it means the internet is available
# -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 (requests.ConnectionError, requests.RequestException):
except subprocess.CalledProcessError:
return False
# Function to get the currently connected SSID using system commands (for Windows)
@@ -55,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")
@@ -64,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:
@@ -79,36 +272,69 @@ def get_connected_network():
return False
except subprocess.CalledProcessError:
return False
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)
# Queue to store log messages
log_messages = deque(maxlen=20)
log_dialog = None
log_text = None
# Function to exit the application
def exit_application(icon, item):
if log_dialog:
log_dialog.destroy()
icon.stop()
os._exit(0)
# Queue to store log messages
log_messages = deque(maxlen=30)
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
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.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:
@@ -124,47 +350,20 @@ 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
log_dialog_open = False
def show_log_dialog():
global log_dialog, log_text, log_dialog_open
if log_dialog_open == False:
log_dialog_open = True
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()
else:
hide_log_dialog()
# Function to hide the log dialog
def hide_log_dialog():
global log_dialog, log_dialog_open
log_dialog_open = False
if log_dialog:
log_dialog.withdraw()
log_dialog = None
# 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, default=True),
pystray.MenuItem('Exit', exit_application)
@@ -175,67 +374,95 @@ def create_system_tray_icon():
# Function to check network status
running = True
errorcount = 0
sleepcount = config["check_every_second"]
sleepcount = check_every_second
connected_ssid_lower = None
def check_network_status():
global running, errorcount, sleepcount, connected_ssid_lower
global running, errorcount, sleepcount, connected_ssid_lower, ssid, response
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) or (connected_ssid == "Ethernet")):
if (is_internet_available()):
sleepcount = config["check_every_second"]
sleepcount = check_every_second
message = "Connected to {} and internet connection is available. Checking again in {} seconds...".format(connected_ssid, str(sleepcount))
errorcount = 0
add_to_log(message)
else:
sleepcount = 3
time.sleep(sleepcount)
message = "Connected to {} but internet is down. Sending the request in {} seconds...".format(connected_ssid, str(sleepcount))
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:
# Update SSL cipher list
try:
requests.packages.urllib3.util.ssl_.DEFAULT_CIPHERS += 'HIGH:!DH:!aNULL'
except AttributeError as e:
# no pyopenssl support used / needed / available
add_to_log("Error when adding to DEFAULT_CIPHERS: {}".format(e))
save_to_file("Error when adding to DEFAULT_CIPHERS: {}".format(e))
pass
time.sleep(1)
response = requests.post(url, data=json.dumps(payload), headers=headers, verify=False, timeout=10)
message = "Request sent."
add_to_log(message)
save_to_file(message)
# Check the response status code
try:
# Function to send request
response = send_request()
if response.ok:
# Success!
errorcount = 0
sleepcount = 10
message = "Request was successful. Connection established! Checking the internet connection 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, str(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, str(sleepcount), str(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 target {} or any Ethernet. Checking again in {} seconds...".format(expected_ssid, str(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)
@@ -248,28 +475,17 @@ def check_network_status():
running = False
os._exit(0)
def open_log_dialog_thread():
log_thread = threading.Thread(target=show_log_dialog)
log_thread.start()
if __name__ == '__main__':
# Create a Tkinter window to run the main loop
root = tk.Tk()
# Create a thread for the system tray icon
tray_thread = threading.Thread(target=create_system_tray_icon)
tray_thread.daemon = True
tray_thread.start()
# Open Log Messages at startup
open_log_dialog_thread()
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()
# Start the Tkinter main loop
root.mainloop()
# Wait for all threads to finish
tray_thread.join()
tray_thread.join()
+61 -24
View File
@@ -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%'>
![Example Log.](/examples/Logs.png)
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,17 +52,54 @@ In the "Payload" tab, you can find your form data or request payload that displa
![Example Request.](/examples/Request.png)
`"ssid":` This is your network's SSID. For example "MyHome_5G"
> [!NOTE]
> [!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:
>
![Example Request.](/examples/config.png)
## 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.
@@ -72,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
View File
@@ -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

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 746 B

+4
View File
@@ -0,0 +1,4 @@
urllib3==1.26.18
requests==2.31.0
pystray==0.19.5
dnspython==2.6.1
+26
View File
@@ -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%