Import source: src/manager

Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality.
This commit is contained in:
arsam
2026-09-18 23:07:18 +03:30
parent 6088044e0f
commit cc3acd1496
+309
View File
@@ -0,0 +1,309 @@
"""
Progress and Cookie Management Module
This module provides functionality for managing application progress, cookies,
and database operations.
Classes:
ProgressManager: Handles progress tracking and persistence
CookieManager: Manages browser cookie operations
DatabaseManager: Handles database interactions
"""
import logging
import os
import pickle
import sqlite3
import sys
import time
from datetime import date
from selenium.common.exceptions import UnableToSetCookieException
from selenium.webdriver.common.by import By
LOGGER_NAME = "ChatGPT-API-Leakage"
LOG_FORMAT = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
logging.basicConfig(level=logging.INFO, format=LOG_FORMAT, datefmt="[%X]")
logger = logging.getLogger(LOGGER_NAME)
class ProgressManagerError(Exception):
"""Custom exception for ProgressManager class errors"""
def __init__(self, message):
super().__init__(message)
class ProgressManager:
"""
Manages and persists progress information for long-running operations.
Attributes:
progress_file (Path): Path to the progress file
Methods:
save: Saves current progress
load: Loads saved progress
"""
def __init__(self, progress_file=".progress.txt"):
"""
Initialize the ProgressManager with a specified progress file.
Args:
progress_file (str): The file where progress data is stored.
"""
self.progress_file = progress_file
def save(self, from_iter: int, total: int):
"""
Saves the current progress to a file.
Args:
from_iter (int): The current iteration progress.
total (int): The total number of iterations.
"""
with open(self.progress_file, "w", encoding="utf-8") as file:
file.write(f"{from_iter}/{total}/{time.time()}")
def load(self, total: int) -> int:
"""
Loads the previously saved progress if available and valid.
Args:
total (int): The total number of iterations for the current process.
Returns:
int: The iteration number to continue from.
"""
if not os.path.exists(self.progress_file):
return 0
with open(self.progress_file, "r", encoding="utf-8") as file:
last_, totl_, tmst_ = file.read().strip().split("/")
last, totl = int(last_), int(totl_)
if time.time() - float(tmst_) < 3600 and totl == total:
action = input(f"🔍 Progress found, do you want to continue from the last progress ({last}/{totl})? [yes] | no: ").lower()
if action in {"yes", "y", ""}:
return last
return 0
class CookieManager:
"""
Manages browser cookie operations.
Methods:
save: Saves cookies to a file
load: Loads cookies from a file
verify_user_login: Checks if the user is currently logged in
"""
def __init__(self, driver):
"""
Initialize the CookieManager with a Selenium WebDriver instance.
Args:
driver (WebDriver): The Selenium WebDriver for cookie operations.
"""
self.driver = driver
def save(self):
"""
Save cookies from the current browser session to a file.
"""
cookies = self.driver.get_cookies()
with open("cookies.pkl", "wb") as file:
pickle.dump(cookies, file)
logger.info("🍪 Cookies saved")
def load(self):
"""
Load cookies from a file and attempt to add them to the current browser session.
"""
try:
with open("cookies.pkl", "rb") as file:
cookies = pickle.load(file)
for cookie in cookies:
try:
self.driver.add_cookie(cookie)
except UnableToSetCookieException:
logger.debug("🟡 Warning, unable to set a cookie %s", cookie)
except (EOFError, pickle.UnpicklingError):
if os.path.exists("cookies.pkl"):
os.remove("cookies.pkl")
logger.error("🔴 Error, unable to load cookies, invalid cookies has been removed, please restart.")
def verify_user_login(self):
"""
Test if the user is really logged in by navigating to GitHub and checking login status.
"""
logger.info("🤗 Redirecting ...")
self.driver.get("https://github.com/")
if self.driver.find_elements(by=By.XPATH, value="//*[contains(text(), 'Sign in')]"):
if os.path.exists("cookies.pkl"):
os.remove("cookies.pkl")
logger.error("🔴 Error, you are not logged in, please restart and try again.")
sys.exit(1)
return True
class DatabaseManager:
"""
This class is used to manage the database, including creating tables and handling data interactions.
"""
def __init__(self, db_filename: str):
"""
Initialize the DatabaseManager with the specified database filename.
Args:
db_filename (str): Path to the SQLite database file.
"""
self.db_filename = db_filename
self.con = None
self.cur = None
def __enter__(self):
"""
Enter the runtime context related to this object, initializing the database if needed.
"""
if not os.path.exists(self.db_filename):
logging.info("Creating database github.db")
self.con = sqlite3.connect(self.db_filename)
self.cur = self.con.cursor()
self.cur.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='APIKeys'")
if self.cur.fetchone() is None:
logging.info("Creating table APIKeys")
self.cur.execute("CREATE TABLE APIKeys(apiKey, status, lastChecked)")
self.cur.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='URLs'")
if self.cur.fetchone() is None:
logging.info("Creating table URLs")
self.cur.execute("CREATE TABLE URLs(url, key)")
return self
def __exit__(self, exc_type, exc_value, traceback):
"""
Exit the runtime context and close the database connection.
"""
if self.con:
self.con.close()
def all_iq_keys(self) -> list:
"""
Get all keys with the status 'insufficient_quota'.
Returns:
list: A list of tuples containing API keys.
"""
if self.cur is None:
raise ValueError("Cursor is not initialized")
self.cur.execute("SELECT apiKey FROM APIKeys WHERE status='insufficient_quota'")
return self.cur.fetchall()
def all_keys(self) -> list:
"""
Get all keys with the status 'yes'.
Returns:
list: A list of tuples containing API keys.
"""
if self.cur is None:
raise ValueError("Cursor is not initialized")
self.cur.execute("SELECT apiKey FROM APIKeys WHERE status='yes'")
return self.cur.fetchall()
def deduplicate(self) -> None:
"""
Deduplicate the 'APIKeys' table by retaining only the latest record for each key.
"""
if self.con is None:
raise ValueError("Connection is not initialized")
if self.cur is None:
raise ValueError("Cursor is not initialized")
self.cur.execute("CREATE TABLE temp_table as SELECT apiKey, status, MAX(lastChecked) as lastChecked FROM APIKeys GROUP BY apiKey;")
self.cur.execute("DROP TABLE APIKeys;")
self.cur.execute("ALTER TABLE temp_table RENAME TO APIKeys;")
self.con.commit()
def delete(self, api_key: str) -> None:
"""
Delete a specific API key from the database.
Args:
api_key (str): The unique API key to remove.
"""
if self.con is None:
raise ValueError("Connection is not initialized")
if self.cur is None:
raise ValueError("Cursor is not initialized")
self.cur.execute("DELETE FROM APIKeys WHERE apiKey=?", (api_key,))
self.con.commit()
def insert(self, api_key: str, status: str):
"""
Insert a new API key and status into the database.
Args:
api_key (str): The API key to insert.
status (str): The status of the API key.
"""
if self.con is None:
raise ValueError("Connection is not initialized")
if self.cur is None:
raise ValueError("Cursor is not initialized")
today = date.today()
self.cur.execute("INSERT INTO APIKeys(apiKey, status, lastChecked) VALUES(?, ?, ?)", (api_key, status, today))
self.con.commit()
def key_exists(self, api_key: str) -> bool:
"""
Check if a given API key exists in the database.
Args:
api_key (str): The API key to search for.
Returns:
bool: True if the API key exists, False otherwise.
"""
if self.cur is None:
raise ValueError("Cursor is not initialized")
self.cur.execute("SELECT apiKey FROM APIKeys WHERE apiKey=?", (api_key,))
return self.cur.fetchone() is not None
def insert_url(self, url: str) -> None:
"""
Insert a new URL into the 'URLs' table.
Args:
url (str): The URL to add.
"""
if self.con is None:
raise ValueError("Connection is not initialized")
if self.cur is None:
raise ValueError("Cursor is not initialized")
self.cur.execute("INSERT INTO URLs(url, key) VALUES(?, ?)", (url, 1))
self.con.commit()
def get_url(self, url: str) -> str | None:
"""
Retrieve the 'key' associated with the given URL.
Args:
url (str): The URL to look up.
Returns:
str | None: The key if it exists, None if not.
"""
if self.cur is None:
raise ValueError("Cursor is not initialized")
self.cur.execute("SELECT key FROM URLs WHERE url=?", (url,))
fetch = self.cur.fetchone()
return fetch[0] if fetch else None