216 lines
6.7 KiB
Python
216 lines
6.7 KiB
Python
import os
|
|
import logging
|
|
from flask import Flask, request, jsonify
|
|
import requests
|
|
from datetime import datetime
|
|
|
|
# Настройка логирования
|
|
logging.basicConfig(
|
|
level=logging.INFO,
|
|
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
|
)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
app = Flask(__name__)
|
|
|
|
# Конфигурация из переменных окружения
|
|
EDU_HOST = os.getenv('EDU_HOST', 'edu.edu.vn.ua')
|
|
EDU_LOGIN = os.getenv('EDU_LOGIN', '')
|
|
EDU_PASSWORD = os.getenv('EDU_PASSWORD', '')
|
|
BOT_PORT = int(os.getenv('BOT_PORT', '5000'))
|
|
BOT_HOST = os.getenv('BOT_HOST', '0.0.0.0')
|
|
|
|
# Кэш для хранения актуальной сессии
|
|
session_cache = {
|
|
'phpsessid': None,
|
|
'expires_at': None
|
|
}
|
|
|
|
|
|
def login_and_get_session():
|
|
"""
|
|
Выполняет логин и возвращает новый PHPSESSID
|
|
"""
|
|
url = f"https://{EDU_HOST}/user/login"
|
|
|
|
headers = {
|
|
'Cache-Control': 'max-age=0',
|
|
'Sec-Ch-Ua': '"Chromium";v="141", "Not?A_Brand";v="8"',
|
|
'Sec-Ch-Ua-Mobile': '?0',
|
|
'Sec-Ch-Ua-Platform': '"Linux"',
|
|
'Accept-Language': 'ru-RU,ru;q=0.9',
|
|
'Origin': f'https://{EDU_HOST}',
|
|
'Content-Type': 'application/x-www-form-urlencoded',
|
|
'Upgrade-Insecure-Requests': '1',
|
|
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36',
|
|
'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',
|
|
'Sec-Fetch-Site': 'same-origin',
|
|
'Sec-Fetch-Mode': 'navigate',
|
|
'Sec-Fetch-User': '?1',
|
|
'Sec-Fetch-Dest': 'document',
|
|
'Referer': f'https://{EDU_HOST}/',
|
|
'Accept-Encoding': 'gzip, deflate, br',
|
|
'Priority': 'u=0, i'
|
|
}
|
|
|
|
data = {
|
|
'login': EDU_LOGIN,
|
|
'password': EDU_PASSWORD
|
|
}
|
|
|
|
try:
|
|
logger.info(f"Attempting login to {url}")
|
|
response = requests.post(
|
|
url,
|
|
data=data,
|
|
headers=headers,
|
|
allow_redirects=False,
|
|
timeout=10
|
|
)
|
|
|
|
# Получаем PHPSESSID из cookies
|
|
phpsessid = response.cookies.get('PHPSESSID')
|
|
|
|
if phpsessid:
|
|
logger.info(f"Login successful, got PHPSESSID: {phpsessid[:10]}...")
|
|
return {
|
|
'success': True,
|
|
'phpsessid': phpsessid,
|
|
'status_code': response.status_code
|
|
}
|
|
else:
|
|
logger.warning(f"Login failed: no PHPSESSID in response. Status: {response.status_code}")
|
|
return {
|
|
'success': False,
|
|
'error': 'No PHPSESSID in response',
|
|
'status_code': response.status_code
|
|
}
|
|
|
|
except requests.exceptions.RequestException as e:
|
|
logger.error(f"Login request failed: {str(e)}")
|
|
return {
|
|
'success': False,
|
|
'error': str(e)
|
|
}
|
|
|
|
|
|
def validate_phpsessid(phpsessid):
|
|
"""
|
|
Проверяет валидность существующего PHPSESSID
|
|
"""
|
|
url = f"https://{EDU_HOST}/"
|
|
|
|
headers = {
|
|
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36',
|
|
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'
|
|
}
|
|
|
|
cookies = {
|
|
'PHPSESSID': phpsessid
|
|
}
|
|
|
|
try:
|
|
response = requests.get(url, headers=headers, cookies=cookies, timeout=10)
|
|
|
|
# Проверяем, не редиректит ли на страницу логина
|
|
is_valid = response.status_code == 200 and '/user/login' not in response.url
|
|
|
|
return {
|
|
'valid': is_valid,
|
|
'status_code': response.status_code,
|
|
'url': response.url
|
|
}
|
|
except requests.exceptions.RequestException as e:
|
|
logger.error(f"Validation request failed: {str(e)}")
|
|
return {
|
|
'valid': False,
|
|
'error': str(e)
|
|
}
|
|
|
|
|
|
@app.route('/health', methods=['GET'])
|
|
def health():
|
|
"""Health check endpoint"""
|
|
return jsonify({'status': 'ok', 'timestamp': datetime.now().isoformat()})
|
|
|
|
|
|
@app.route('/get-session', methods=['POST', 'GET'])
|
|
def get_session():
|
|
"""
|
|
Основной endpoint для получения валидного PHPSESSID
|
|
Возвращает кэшированную сессию или создает новую
|
|
"""
|
|
result = login_and_get_session()
|
|
|
|
if result['success']:
|
|
session_cache['phpsessid'] = result['phpsessid']
|
|
session_cache['last_updated'] = datetime.now().isoformat()
|
|
|
|
return jsonify({
|
|
'success': True,
|
|
'phpsessid': result['phpsessid'],
|
|
'timestamp': datetime.now().isoformat()
|
|
})
|
|
else:
|
|
return jsonify({
|
|
'success': False,
|
|
'error': result.get('error', 'Login failed'),
|
|
'timestamp': datetime.now().isoformat()
|
|
}), 400
|
|
|
|
|
|
@app.route('/validate-session', methods=['POST'])
|
|
def validate_session():
|
|
"""
|
|
Проверяет валидность переданного PHPSESSID
|
|
"""
|
|
data = request.get_json() or {}
|
|
phpsessid = data.get('phpsessid') or request.args.get('phpsessid')
|
|
|
|
if not phpsessid:
|
|
return jsonify({
|
|
'success': False,
|
|
'error': 'PHPSESSID not provided'
|
|
}), 400
|
|
|
|
validation_result = validate_phpsessid(phpsessid)
|
|
|
|
return jsonify({
|
|
'success': True,
|
|
'valid': validation_result.get('valid', False),
|
|
'details': validation_result,
|
|
'timestamp': datetime.now().isoformat()
|
|
})
|
|
|
|
|
|
@app.route('/refresh-session', methods=['POST', 'GET'])
|
|
def refresh_session():
|
|
"""
|
|
Принудительно обновляет сессию
|
|
"""
|
|
result = login_and_get_session()
|
|
|
|
if result['success']:
|
|
return jsonify({
|
|
'success': True,
|
|
'phpsessid': result['phpsessid'],
|
|
'message': 'Session refreshed successfully',
|
|
'timestamp': datetime.now().isoformat()
|
|
})
|
|
else:
|
|
return jsonify({
|
|
'success': False,
|
|
'error': result.get('error', 'Failed to refresh session'),
|
|
'timestamp': datetime.now().isoformat()
|
|
}), 400
|
|
|
|
|
|
if __name__ == '__main__':
|
|
if not EDU_LOGIN or not EDU_PASSWORD:
|
|
logger.error("EDU_LOGIN and EDU_PASSWORD must be set!")
|
|
exit(1)
|
|
|
|
logger.info(f"Starting PHPSESSID validator bot on {BOT_HOST}:{BOT_PORT}")
|
|
logger.info(f"Target host: {EDU_HOST}")
|
|
|
|
app.run(host=BOT_HOST, port=BOT_PORT, debug=False) |