Update renewer.py
Browse files- renewer.py +67 -52
renewer.py
CHANGED
|
@@ -27,7 +27,6 @@ def save_accounts(accounts):
|
|
| 27 |
|
| 28 |
|
| 29 |
def get_host(username, token):
|
| 30 |
-
"""اكتشاف الخادم (US أو EU) عبر الـ Token"""
|
| 31 |
for host in ["www.pythonanywhere.com", "eu.pythonanywhere.com"]:
|
| 32 |
try:
|
| 33 |
r = requests.get(
|
|
@@ -43,61 +42,81 @@ def get_host(username, token):
|
|
| 43 |
|
| 44 |
|
| 45 |
def extend_webapp(username, password, host="www.pythonanywhere.com"):
|
| 46 |
-
"""
|
| 47 |
-
التمديد الحقيقي عبر session + CSRF
|
| 48 |
-
يضغط زر Extend تماماً كما لو فعلته يدوياً
|
| 49 |
-
"""
|
| 50 |
session = requests.Session()
|
| 51 |
results = []
|
| 52 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 53 |
try:
|
| 54 |
-
# ── الخطوة 1: فتح صفحة تسجيل الدخول
|
| 55 |
login_url = f"https://{host}/login/"
|
| 56 |
login_page = session.get(login_url, timeout=15)
|
| 57 |
logger.info(f"[{username}] صفحة الدخول: {login_page.status_code}")
|
| 58 |
|
| 59 |
soup = BeautifulSoup(login_page.text, "html.parser")
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
|
|
|
|
|
|
| 63 |
return []
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
"
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 74 |
login_resp = session.post(
|
| 75 |
-
|
| 76 |
-
data=
|
| 77 |
headers={
|
| 78 |
"Referer": login_url,
|
| 79 |
-
"Content-Type": "application/x-www-form-urlencoded"
|
|
|
|
| 80 |
},
|
| 81 |
timeout=15,
|
| 82 |
allow_redirects=True
|
| 83 |
)
|
| 84 |
-
logger.info(f"[{username}] نتيجة
|
| 85 |
|
| 86 |
# ── التحقق من نجاح تسجيل الدخول ──
|
| 87 |
-
# الطريقة الصحيحة: نتحقق أن URL النهائي ليس صفحة login
|
| 88 |
-
final_url = login_resp.url
|
| 89 |
-
if "login" in final_url.lower():
|
| 90 |
-
logger.error(f"[{username}] فشل تسجيل الدخول — ما زال على صفحة login")
|
| 91 |
-
return []
|
| 92 |
-
|
| 93 |
-
# تحقق إضافي: هل يوجد sessionid في الـ cookies؟
|
| 94 |
cookies = session.cookies.get_dict()
|
| 95 |
logger.info(f"[{username}] الكوكيز: {list(cookies.keys())}")
|
|
|
|
| 96 |
if "sessionid" not in cookies:
|
| 97 |
-
logger.error(f"[{username}] لم يتم إنشاء
|
| 98 |
return []
|
| 99 |
|
| 100 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 101 |
|
| 102 |
# ── الخطوة 3: فتح صفحة الويب آب وجلب CSRF جديد ──
|
| 103 |
webapps_url = f"https://{host}/user/{username}/webapps/"
|
|
@@ -107,18 +126,17 @@ def extend_webapp(username, password, host="www.pythonanywhere.com"):
|
|
| 107 |
soup = BeautifulSoup(page.text, "html.parser")
|
| 108 |
csrf_input = soup.find("input", {"name": "csrfmiddlewaretoken"})
|
| 109 |
if not csrf_input:
|
| 110 |
-
# محاولة بديلة من الـ meta tag
|
| 111 |
meta = soup.find("meta", {"name": "csrf-token"})
|
| 112 |
-
if meta
|
| 113 |
-
csrf = meta.get("content", "")
|
| 114 |
-
else:
|
| 115 |
-
logger.error(f"[{username}] لم يتم العثور على CSRF في صفحة الويب آب")
|
| 116 |
-
return []
|
| 117 |
else:
|
| 118 |
csrf = csrf_input["value"]
|
| 119 |
-
logger.info(f"[{username}] CSRF الثاني: {csrf[:10]}...")
|
| 120 |
|
| 121 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 122 |
domains = []
|
| 123 |
for link in soup.find_all("a", href=True):
|
| 124 |
href = link["href"]
|
|
@@ -129,11 +147,11 @@ def extend_webapp(username, password, host="www.pythonanywhere.com"):
|
|
| 129 |
|
| 130 |
if not domains:
|
| 131 |
domains = [f"{username}.pythonanywhere.com"]
|
| 132 |
-
logger.warning(f"[{username}]
|
| 133 |
else:
|
| 134 |
-
logger.info(f"[{username}] النطاقات
|
| 135 |
|
| 136 |
-
# ── الخطوة 5: إرسال طلب التمديد
|
| 137 |
for domain in domains:
|
| 138 |
extend_url = f"https://{host}/user/{username}/webapps/{domain}/extend"
|
| 139 |
resp = session.post(
|
|
@@ -141,21 +159,18 @@ def extend_webapp(username, password, host="www.pythonanywhere.com"):
|
|
| 141 |
data={"csrfmiddlewaretoken": csrf},
|
| 142 |
headers={
|
| 143 |
"Referer": webapps_url,
|
| 144 |
-
"Content-Type": "application/x-www-form-urlencoded"
|
|
|
|
| 145 |
},
|
| 146 |
timeout=15
|
| 147 |
)
|
| 148 |
logger.info(f"[{username}] Extend {domain}: HTTP {resp.status_code}")
|
| 149 |
|
| 150 |
-
if resp.status_code in (200, 302)
|
| 151 |
-
status = "✅ تم التمديد"
|
| 152 |
-
else:
|
| 153 |
-
status = f"❌ فشل ({resp.status_code})"
|
| 154 |
-
|
| 155 |
results.append({"domain": domain, "status": status})
|
| 156 |
|
| 157 |
except Exception as e:
|
| 158 |
-
logger.error(f"[{username}] خطأ
|
| 159 |
|
| 160 |
return results
|
| 161 |
|
|
@@ -166,7 +181,7 @@ def renew_account(account):
|
|
| 166 |
host = account.get("host", "www.pythonanywhere.com")
|
| 167 |
|
| 168 |
if not password:
|
| 169 |
-
logger.error(f"[{username}] لا توجد كلمة مرور
|
| 170 |
return False, []
|
| 171 |
|
| 172 |
results = extend_webapp(username, password, host)
|
|
@@ -197,7 +212,7 @@ def run_renewals():
|
|
| 197 |
next_renewal = datetime.fromisoformat(next_renewal_str)
|
| 198 |
if now < next_renewal:
|
| 199 |
remaining = (next_renewal - now).days
|
| 200 |
-
logger.info(f"[{username}]
|
| 201 |
continue
|
| 202 |
|
| 203 |
logger.info(f"[{username}] جاري التمديد...")
|
|
|
|
| 27 |
|
| 28 |
|
| 29 |
def get_host(username, token):
|
|
|
|
| 30 |
for host in ["www.pythonanywhere.com", "eu.pythonanywhere.com"]:
|
| 31 |
try:
|
| 32 |
r = requests.get(
|
|
|
|
| 42 |
|
| 43 |
|
| 44 |
def extend_webapp(username, password, host="www.pythonanywhere.com"):
|
|
|
|
|
|
|
|
|
|
|
|
|
| 45 |
session = requests.Session()
|
| 46 |
results = []
|
| 47 |
|
| 48 |
+
# headers تحاكي المتصفح تماماً
|
| 49 |
+
session.headers.update({
|
| 50 |
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
|
| 51 |
+
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
| 52 |
+
"Accept-Language": "en-US,en;q=0.9",
|
| 53 |
+
})
|
| 54 |
+
|
| 55 |
try:
|
| 56 |
+
# ── الخطوة 1: فتح صفحة تسجيل الدخول ──
|
| 57 |
login_url = f"https://{host}/login/"
|
| 58 |
login_page = session.get(login_url, timeout=15)
|
| 59 |
logger.info(f"[{username}] صفحة الدخول: {login_page.status_code}")
|
| 60 |
|
| 61 |
soup = BeautifulSoup(login_page.text, "html.parser")
|
| 62 |
+
|
| 63 |
+
# اجلب كل حقول الـ form تلقائياً
|
| 64 |
+
form = soup.find("form")
|
| 65 |
+
if not form:
|
| 66 |
+
logger.error(f"[{username}] لم يتم العثور على form في صفحة الدخول")
|
| 67 |
return []
|
| 68 |
+
|
| 69 |
+
# ابدأ بكل الحقول الموجودة في الـ form (hidden fields)
|
| 70 |
+
form_data = {}
|
| 71 |
+
for inp in form.find_all("input"):
|
| 72 |
+
name = inp.get("name")
|
| 73 |
+
value = inp.get("value", "")
|
| 74 |
+
if name:
|
| 75 |
+
form_data[name] = value
|
| 76 |
+
|
| 77 |
+
logger.info(f"[{username}] حقول الـ form: {list(form_data.keys())}")
|
| 78 |
+
|
| 79 |
+
# أضف بيانات الدخول
|
| 80 |
+
form_data["auth-username"] = username
|
| 81 |
+
form_data["auth-password"] = password
|
| 82 |
+
|
| 83 |
+
# بعض إصدارات الموقع تحتاج هذا الحقل
|
| 84 |
+
if "login_view-current_step" not in form_data:
|
| 85 |
+
form_data["login_view-current_step"] = "auth"
|
| 86 |
+
|
| 87 |
+
# ── الخطوة 2: إرسال بيانات الدخول ──
|
| 88 |
+
form_action = form.get("action") or login_url
|
| 89 |
+
if not form_action.startswith("http"):
|
| 90 |
+
form_action = f"https://{host}{form_action}"
|
| 91 |
+
|
| 92 |
+
logger.info(f"[{username}] إرسال الدخول إلى: {form_action}")
|
| 93 |
+
|
| 94 |
login_resp = session.post(
|
| 95 |
+
form_action,
|
| 96 |
+
data=form_data,
|
| 97 |
headers={
|
| 98 |
"Referer": login_url,
|
| 99 |
+
"Content-Type": "application/x-www-form-urlencoded",
|
| 100 |
+
"Origin": f"https://{host}",
|
| 101 |
},
|
| 102 |
timeout=15,
|
| 103 |
allow_redirects=True
|
| 104 |
)
|
| 105 |
+
logger.info(f"[{username}] نتيجة الدخول: {login_resp.status_code} | URL: {login_resp.url}")
|
| 106 |
|
| 107 |
# ── التحقق من نجاح تسجيل الدخول ──
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 108 |
cookies = session.cookies.get_dict()
|
| 109 |
logger.info(f"[{username}] الكوكيز: {list(cookies.keys())}")
|
| 110 |
+
|
| 111 |
if "sessionid" not in cookies:
|
| 112 |
+
logger.error(f"[{username}] لم يتم إنشاء sessionid — فشل تسجيل الدخول")
|
| 113 |
return []
|
| 114 |
|
| 115 |
+
if "login" in login_resp.url.lower() and login_resp.status_code != 200:
|
| 116 |
+
logger.error(f"[{username}] ما زال على صفحة login — فشل")
|
| 117 |
+
return []
|
| 118 |
+
|
| 119 |
+
logger.info(f"[{username}] ✅ تم تسجيل الدخول بنجاح")
|
| 120 |
|
| 121 |
# ── الخطوة 3: فتح صفحة الويب آب وجلب CSRF جديد ──
|
| 122 |
webapps_url = f"https://{host}/user/{username}/webapps/"
|
|
|
|
| 126 |
soup = BeautifulSoup(page.text, "html.parser")
|
| 127 |
csrf_input = soup.find("input", {"name": "csrfmiddlewaretoken"})
|
| 128 |
if not csrf_input:
|
|
|
|
| 129 |
meta = soup.find("meta", {"name": "csrf-token"})
|
| 130 |
+
csrf = meta.get("content", "") if meta else ""
|
|
|
|
|
|
|
|
|
|
|
|
|
| 131 |
else:
|
| 132 |
csrf = csrf_input["value"]
|
|
|
|
| 133 |
|
| 134 |
+
if not csrf:
|
| 135 |
+
logger.error(f"[{username}] لم يتم العثور على CSRF في صفحة الويب آب")
|
| 136 |
+
return []
|
| 137 |
+
logger.info(f"[{username}] CSRF الويب آب: {csrf[:10]}...")
|
| 138 |
+
|
| 139 |
+
# ── الخطوة 4: اكتشاف النطاقات ──
|
| 140 |
domains = []
|
| 141 |
for link in soup.find_all("a", href=True):
|
| 142 |
href = link["href"]
|
|
|
|
| 147 |
|
| 148 |
if not domains:
|
| 149 |
domains = [f"{username}.pythonanywhere.com"]
|
| 150 |
+
logger.warning(f"[{username}] النطاق الافتراضي: {domains[0]}")
|
| 151 |
else:
|
| 152 |
+
logger.info(f"[{username}] النطاقات: {domains}")
|
| 153 |
|
| 154 |
+
# ── الخطوة 5: إرسال طلب التمديد ──
|
| 155 |
for domain in domains:
|
| 156 |
extend_url = f"https://{host}/user/{username}/webapps/{domain}/extend"
|
| 157 |
resp = session.post(
|
|
|
|
| 159 |
data={"csrfmiddlewaretoken": csrf},
|
| 160 |
headers={
|
| 161 |
"Referer": webapps_url,
|
| 162 |
+
"Content-Type": "application/x-www-form-urlencoded",
|
| 163 |
+
"Origin": f"https://{host}",
|
| 164 |
},
|
| 165 |
timeout=15
|
| 166 |
)
|
| 167 |
logger.info(f"[{username}] Extend {domain}: HTTP {resp.status_code}")
|
| 168 |
|
| 169 |
+
status = "✅ تم التمديد" if resp.status_code in (200, 302) else f"❌ فشل ({resp.status_code})"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 170 |
results.append({"domain": domain, "status": status})
|
| 171 |
|
| 172 |
except Exception as e:
|
| 173 |
+
logger.error(f"[{username}] خطأ: {e}")
|
| 174 |
|
| 175 |
return results
|
| 176 |
|
|
|
|
| 181 |
host = account.get("host", "www.pythonanywhere.com")
|
| 182 |
|
| 183 |
if not password:
|
| 184 |
+
logger.error(f"[{username}] لا توجد كلمة مرور")
|
| 185 |
return False, []
|
| 186 |
|
| 187 |
results = extend_webapp(username, password, host)
|
|
|
|
| 212 |
next_renewal = datetime.fromisoformat(next_renewal_str)
|
| 213 |
if now < next_renewal:
|
| 214 |
remaining = (next_renewal - now).days
|
| 215 |
+
logger.info(f"[{username}] التجديد بعد {remaining} يوم")
|
| 216 |
continue
|
| 217 |
|
| 218 |
logger.info(f"[{username}] جاري التمديد...")
|