Does this python script look good to you?
Wondering if there's any room for improvement.. does it look ok to you? It's supposed to check the webpage every second or so and alert me if there's change. Trying to make it as human-like as possible and not to stressful on the webpage it's checking. Any advice appreciated.
ATM it works but every minute or so there's a few seconds of ERROR status.
import requests, tkinter as tk, os, random, time, json, subprocess
from datetime import datetime
from threading import Thread, Lock
from tkinter import ttk
# -------------------------
# CONFIGURATION
# -------------------------
URL = "https://formdt1.com/products/t1titanium.js" # <-- CHANGE THIS
DEBUG = True
FLASH_DURATION = 500 # milliseconds
STATE_FILE = "last_state.json" # persistent state file
# -------------------------
# USER AGENTS
# -------------------------
USER_AGENTS = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 13_0) AppleWebKit/537.36 Chrome/119 Safari/537.36",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 Chrome/118 Safari/537.36"
]
# -------------------------
# SESSION
# -------------------------
session = requests.Session()
# -------------------------
# STATE VARIABLES
# -------------------------
data_state = {} # Current availability
last_state = {} # Previous availability
newly_available = set() # Track newly available variants
state_lock = Lock()
status_text = "Starting..."
system_status = "STARTING"
last_update = "N/A"
last_check_time = None
etag = None
error_count = 0
# -------------------------
# ALERT FUNCTION (non-blocking)
# -------------------------
def alert(msg="Test alert"):
"""
Sends a system notification + sound (non-blocking)
"""
subprocess.Popen(['notify-send', f'⚠️ {msg}'])
subprocess.Popen(['paplay', '/usr/share/sounds/freedesktop/stereo/alarm-clock-elapsed.oga'])
if DEBUG:
print(f"[ALERT] {msg}")
# -------------------------
# VARIANT EXTRACTION
# -------------------------
def extract_variants(data):
"""
Converts JSON into: { "Variant Name": True/False }
"""
return {v["title"]: v["available"] for v in data.get("variants", [])}
# -------------------------
# LOAD/SAVE STATE
# -------------------------
def load_state():
global last_state
if os.path.exists(STATE_FILE):
try:
with open(STATE_FILE, "r") as f:
last_state.update(json.load(f))
except Exception as e:
print(f"[WARN] Failed to load state: {e}")
def save_state():
with state_lock:
try:
with open(STATE_FILE, "w") as f:
json.dump(data_state, f)
except Exception as e:
print(f"[WARN] Failed to save state: {e}")
load_state()
# -------------------------
# MONITOR THREAD
# -------------------------
def monitor():
global data_state, last_state, newly_available
global status_text, system_status, last_update, last_check_time, etag, error_count
while True:
try:
headers = {
"User-Agent": random.choice(USER_AGENTS),
"Accept": random.choice([
"application/json, text/javascript, */*; q=0.01",
"*/*"
]),
"Accept-Language": random.choice(["en-US,en;q=0.9", "en-GB,en;q=0.8"]),
"Referer": URL,
"Cache-Control": random.choice(["no-cache", "max-age=0"]),
"Connection": "keep-alive"
}
if etag:
headers["If-None-Match"] = etag
if DEBUG:
print(f"[{datetime.now()}] Checking server...")
r = session.get(URL, headers=headers, timeout=3)
last_check_time = datetime.now()
# -------------------------
# HANDLE RATE LIMIT / BLOCK
# -------------------------
if r.status_code == 429:
system_status = "RATE LIMITED"
status_text = "Too many requests"
error_count += 1
time.sleep(min(60, 2 ** error_count))
continue
elif r.status_code == 403:
system_status = "BLOCKED"
status_text = "Access denied"
error_count += 1
time.sleep(min(60, 2 ** error_count))
continue
# -------------------------
# SUCCESS CASES
# -------------------------
elif r.status_code == 304:
system_status = "OK"
status_text = "NO CHANGE (cached)"
last_update = str(datetime.now())
error_count = 0
elif r.status_code == 200:
system_status = "OK"
error_count = 0
etag = r.headers.get("ETag")
try:
json_data = r.json()
except Exception:
system_status = "ERROR"
status_text = "INVALID JSON"
time.sleep(2)
continue
current = extract_variants(json_data)
with state_lock:
newly_available.clear()
if not last_state:
last_state = current.copy()
data_state = current
status_text = "INIT"
else:
changes = []
for name, available in current.items():
if name in last_state and available != last_state[name]:
if available:
changes.append(f"{name} AVAILABLE")
newly_available.add(name)
if changes:
msg = " | ".join(changes)
status_text = msg
alert(msg)
data_state = current
last_state = current.copy()
save_state()
last_update = str(datetime.now())
else:
system_status = "ERROR"
status_text = f"HTTP {r.status_code}"
error_count += 1
time.sleep(2)
continue
except Exception as e:
system_status = "ERROR"
status_text = f"ERROR: {e}"
error_count += 1
if DEBUG:
print(f"[ERROR] {e}")
time.sleep(2)
continue
# -------------------------
# HUMAN-LIKE DELAY
# -------------------------
delay = random.uniform(0.8, 2.2)
time.sleep(delay)
# -------------------------
# GUI SETUP
# -------------------------
root = tk.Tk()
root.title("Variant Monitor")
root.geometry("1000x520")
root.configure(bg="#f0f0f0")
canvas = tk.Canvas(root, bg="#f0f0f0", highlightthickness=0)
scrollbar = ttk.Scrollbar(root, orient="vertical", command=canvas.yview)
frame = tk.Frame(canvas, bg="#f0f0f0")
frame.bind("", lambda e: canvas.configure(scrollregion=canvas.bbox("all")))
canvas.create_window((0, 0), window=frame, anchor="nw")
canvas.configure(yscrollcommand=scrollbar.set)
canvas.pack(side="left", fill="both", expand=True)
scrollbar.pack(side="right", fill="y")
FONT_TITLE = ("Arial", 12, "bold")
FONT_TEXT = ("Arial", 11)
# -------------------------
# STATUS COLOR
# -------------------------
def status_color():
if system_status == "OK":
return "#28a745"
elif system_status == "RATE LIMITED":
return "#ff9900"
elif system_status == "BLOCKED":
return "#ff0000"
else:
return "#666666"
# -------------------------
# TEST ALERT BUTTON
# -------------------------
btn = tk.Button(root, text="Test Alert",
command=lambda: alert("Test alert"),
bg="#ff9500", fg="black",
font=("Arial", 12, "bold"))
btn.place(x=850, y=10)
# -------------------------
# GUI UPDATE LOOP
# -------------------------
def update_ui():
with state_lock:
local_data = data_state.copy()
local_new = newly_available.copy()
for widget in frame.winfo_children():
widget.destroy()
tk.Label(frame, text=f"System Status: {system_status}",
fg=status_color(), bg="#f0f0f0", font=FONT_TITLE).pack(anchor="w")
# Legend
legend = tk.Frame(frame, bg="#f0f0f0")
legend.pack(anchor="w", pady=(5,10))
tk.Label(legend, text="Legend:", bg="#f0f0f0", font=FONT_TITLE).pack(side="left")
tk.Label(legend, text=" ● OK", fg="#28a745", bg="#f0f0f0").pack(side="left")
tk.Label(legend, text=" ● RATE LIMITED", fg="#ff9900", bg="#f0f0f0").pack(side="left")
tk.Label(legend, text=" ● BLOCKED", fg="#ff0000", bg="#f0f0f0").pack(side="left")
tk.Label(legend, text=" ● ERROR", fg="#666666", bg="#f0f0f0").pack(side="left")
tk.Label(frame, text=f"Last update: {last_update}", bg="#f0f0f0").pack(anchor="w")
tk.Label(frame, text=f"Status: {status_text}", bg="#f0f0f0").pack(anchor="w")
if last_check_time:
seconds = (datetime.now() - last_check_time).total_seconds()
tk.Label(frame, text=f"Time since last check: {seconds:.1f}s", bg="#f0f0f0").pack(anchor="w")
tk.Label(frame, text="", bg="#f0f0f0").pack()
for idx, (name, available) in enumerate(sorted(local_data.items(), key=lambda x: not x[1])):
bg = "#e8e8e8" if idx % 2 == 0 else "#dcdcdc"
color = "#28a745" if available else "#ff4d4d"
text = "AVAILABLE" if available else "NOT AVAILABLE"
row = tk.Frame(frame, bg=bg)
row.pack(fill="x", padx=5, pady=1)
tk.Label(row, text=name, bg=bg, fg="black",
font=FONT_TITLE, anchor="w", width=70).pack(side="left")
status_lbl = tk.Label(row, text=text, bg=bg, fg=color, font=FONT_TITLE)
status_lbl.pack(side="right")
# Flash if newly available
if name in local_new:
def flash(lbl=status_lbl, original=bg):
lbl.config(bg="#28a745")
root.after(FLASH_DURATION, lambda: lbl.config(bg=original))
flash()
root.after(300, update_ui)
# -------------------------
# START PROGRAM
# -------------------------
Thread(target=monitor, daemon=True).start()
update_ui()
root.mainloop()