import logging import os import time from datetime import datetime import redis import requests # Configure logging logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') logger = logging.getLogger(__name__) # Load configuration (adapted to .env keys) def _env(key, default=None): v = os.getenv(key, default) if isinstance(v, str) and len(v) >= 2 and ((v[0] == '"' and v[-1] == '"') or (v[0] == "'" and v[-1] == "'")): return v[1:-1] return v LOGIN = _env('KEEPER_LOGIN') PASSWORD = _env('KEEPER_PASSWORD') EDU_BASE = _env('EDU_URL_BASE', 'https://edu.edu.vn.ua') EDU_LOGIN_PATH = _env('EDU_URL_LOGIN', '/user/login') EDU_COURSES_PATH = _env('EDU_URL_COURSES', '/course/userlist') URL_LOGIN = f'{EDU_BASE.rstrip("/")}/{EDU_LOGIN_PATH.lstrip("/")}' URL_VERIFY = f'{EDU_BASE.rstrip("/")}/{EDU_COURSES_PATH.lstrip("/")}' INTERVAL = int(_env('KEEPER_INTERVAL', 10)) USER_AGENT = _env( 'USER_AGENT', 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36', ) REDIS_HOST = _env('REDIS_HOST', 'redis') REDIS_PORT = int(_env('REDIS_PORT', 6379)) SUCCESS_FILE = '/tmp/last_success' # noqa: S108 def touch_success_file(): """Updates the timestamp of the success file for healthchecks.""" try: with open(SUCCESS_FILE, 'w') as f: f.write(str(datetime.now().timestamp())) except Exception as e: logger.error(f'Failed to touch success file: {e}') def main(): logger.info('Starting Session Keeper Bot') # Connect to Redis try: redis_client = redis.Redis(host=REDIS_HOST, port=REDIS_PORT, decode_responses=True) redis_client.ping() logger.info(f'Connected to Redis at {REDIS_HOST}:{REDIS_PORT}') except Exception as e: logger.error(f'Failed to connect to Redis: {e}') return session = requests.Session() # Set headers headers = { 'User-Agent': USER_AGENT, 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7', 'Accept-Language': 'en-US,en;q=0.9', 'Cache-Control': 'max-age=0', 'Upgrade-Insecure-Requests': '1', 'Sec-Fetch-Site': 'same-origin', 'Sec-Fetch-Mode': 'navigate', 'Sec-Fetch-User': '?1', 'Sec-Fetch-Dest': 'document', 'Sec-Ch-Ua': '"Not_A Brand";v="99", "Chromium";v="142"', 'Sec-Ch-Ua-Mobile': '?0', 'Sec-Ch-Ua-Platform': '"Linux"', 'Accept-Encoding': 'gzip, deflate, br', 'Priority': 'u=0, i', } session.headers.update(headers) while True: try: logger.info('Attempting login...') # Login payload payload = {'login': LOGIN, 'password': PASSWORD} # Perform Login # Note: The user request shows a POST to /user/login with form data # We need to make sure we handle the PHPSESSID correctly. # If we already have a PHPSESSID, requests will send it. login_response = session.post(URL_LOGIN, data=payload, allow_redirects=True) logger.info(f'Login Response Status: {login_response.status_code}') logger.info(f'Cookies after login: {session.cookies.get_dict()}') # Verify Session logger.info('Verifying session...') verify_response = session.get(URL_VERIFY, allow_redirects=False) logger.info(f'Verify Response Status: {verify_response.status_code}') if verify_response.status_code == 200: logger.info('Session verification SUCCESS (200 OK).') touch_success_file() # Save PHPSESSID to Redis phpsessid = session.cookies.get('PHPSESSID') if phpsessid: try: redis_client.set('EDU_PHPSESSID', phpsessid) logger.info(f'Saved PHPSESSID to Redis: {phpsessid}') except Exception as e: logger.error(f'Failed to save PHPSESSID to Redis: {e}') elif verify_response.status_code == 302: logger.warning('Session verification FAILED (302 Redirect). Session might be invalid.') else: logger.warning(f'Session verification returned unexpected status: {verify_response.status_code}') except Exception as e: logger.error(f'An error occurred: {e}') logger.info(f'Sleeping for {INTERVAL} minutes...') time.sleep(INTERVAL * 60) if __name__ == '__main__': main()