重构更新账号日志功能,添加开发者模式
This commit is contained in:
+104
-2
@@ -64,7 +64,8 @@ REQUEST_TIMEOUT_SECONDS = 45
|
||||
FETCH_LIMIT_DEFAULT = 5
|
||||
BASE_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
|
||||
ACCOUNT_LOG_PATH = os.path.join(BASE_DIR, "data", "account-run-history.txt")
|
||||
ACCOUNT_LOG_LOCK = threading.Lock()
|
||||
ACCOUNT_RECORDS_SNAPSHOT_PATH = os.path.join(BASE_DIR, "data", "account-run-history.json")
|
||||
ACCOUNT_RECORDS_LOCK = threading.Lock()
|
||||
|
||||
|
||||
def json_response(handler, status, payload):
|
||||
@@ -131,12 +132,104 @@ def append_account_log(email_addr, password, status, recorded_at="", reason=""):
|
||||
|
||||
os.makedirs(os.path.dirname(ACCOUNT_LOG_PATH), exist_ok=True)
|
||||
line = f"{normalized_recorded_at}\t{normalized_email}\t{normalized_password}\t{normalized_status}\t{normalized_reason}\n"
|
||||
with ACCOUNT_LOG_LOCK:
|
||||
with ACCOUNT_RECORDS_LOCK:
|
||||
with open(ACCOUNT_LOG_PATH, "a", encoding="utf-8") as handle:
|
||||
handle.write(line)
|
||||
return ACCOUNT_LOG_PATH
|
||||
|
||||
|
||||
def normalize_account_run_snapshot_record(record):
|
||||
if not isinstance(record, dict):
|
||||
return None
|
||||
|
||||
email_addr = str(record.get("email") or "").strip()
|
||||
password = str(record.get("password") or "").strip()
|
||||
final_status = str(record.get("finalStatus") or "").strip().lower()
|
||||
if not email_addr or not password or final_status not in {"success", "failed"}:
|
||||
return None
|
||||
|
||||
finished_at = str(record.get("finishedAt") or "").strip() or datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
|
||||
retry_count = max(0, int(record.get("retryCount") or 0))
|
||||
failed_step_raw = record.get("failedStep")
|
||||
try:
|
||||
failed_step = int(failed_step_raw)
|
||||
except (TypeError, ValueError):
|
||||
failed_step = None
|
||||
if failed_step is not None and failed_step <= 0:
|
||||
failed_step = None
|
||||
|
||||
auto_run_context = record.get("autoRunContext") if isinstance(record.get("autoRunContext"), dict) else None
|
||||
normalized_auto_run_context = None
|
||||
if auto_run_context:
|
||||
normalized_auto_run_context = {
|
||||
"currentRun": max(0, int(auto_run_context.get("currentRun") or 0)),
|
||||
"totalRuns": max(0, int(auto_run_context.get("totalRuns") or 0)),
|
||||
"attemptRun": max(0, int(auto_run_context.get("attemptRun") or 0)),
|
||||
}
|
||||
if not any(normalized_auto_run_context.values()):
|
||||
normalized_auto_run_context = None
|
||||
|
||||
source = "auto" if str(record.get("source") or "").strip().lower() == "auto" else "manual"
|
||||
|
||||
return {
|
||||
"recordId": str(record.get("recordId") or email_addr).strip() or email_addr,
|
||||
"email": email_addr,
|
||||
"password": password,
|
||||
"finalStatus": final_status,
|
||||
"finishedAt": finished_at,
|
||||
"retryCount": retry_count,
|
||||
"failureLabel": str(record.get("failureLabel") or "").strip(),
|
||||
"failureDetail": str(record.get("failureDetail") or "").strip(),
|
||||
"failedStep": failed_step,
|
||||
"source": source,
|
||||
"autoRunContext": normalized_auto_run_context,
|
||||
}
|
||||
|
||||
|
||||
def summarize_account_run_snapshot(records):
|
||||
summary = {
|
||||
"total": 0,
|
||||
"success": 0,
|
||||
"failed": 0,
|
||||
"retryTotal": 0,
|
||||
}
|
||||
for item in records:
|
||||
summary["total"] += 1
|
||||
if item.get("finalStatus") == "success":
|
||||
summary["success"] += 1
|
||||
elif item.get("finalStatus") == "failed":
|
||||
summary["failed"] += 1
|
||||
summary["retryTotal"] += max(0, int(item.get("retryCount") or 0))
|
||||
return summary
|
||||
|
||||
|
||||
def normalize_account_run_snapshot_payload(payload):
|
||||
if not isinstance(payload, dict):
|
||||
raise RuntimeError("Invalid account run snapshot payload")
|
||||
|
||||
normalized_records = []
|
||||
for item in payload.get("records") if isinstance(payload.get("records"), list) else []:
|
||||
normalized = normalize_account_run_snapshot_record(item)
|
||||
if normalized:
|
||||
normalized_records.append(normalized)
|
||||
|
||||
return {
|
||||
"generatedAt": str(payload.get("generatedAt") or "").strip() or datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"),
|
||||
"summary": summarize_account_run_snapshot(normalized_records),
|
||||
"records": normalized_records,
|
||||
}
|
||||
|
||||
|
||||
def sync_account_run_records(payload):
|
||||
normalized_payload = normalize_account_run_snapshot_payload(payload)
|
||||
os.makedirs(os.path.dirname(ACCOUNT_RECORDS_SNAPSHOT_PATH), exist_ok=True)
|
||||
with ACCOUNT_RECORDS_LOCK:
|
||||
with open(ACCOUNT_RECORDS_SNAPSHOT_PATH, "w", encoding="utf-8") as handle:
|
||||
json.dump(normalized_payload, handle, ensure_ascii=False, indent=2)
|
||||
handle.write("\n")
|
||||
return ACCOUNT_RECORDS_SNAPSHOT_PATH
|
||||
|
||||
|
||||
def try_refresh_access_token(endpoint, client_id, refresh_token):
|
||||
request_data = {
|
||||
"client_id": client_id,
|
||||
@@ -625,6 +718,14 @@ class HotmailHelperHandler(BaseHTTPRequestHandler):
|
||||
try:
|
||||
payload = read_json_payload(self)
|
||||
|
||||
if self.path == "/sync-account-run-records":
|
||||
file_path = sync_account_run_records(payload)
|
||||
json_response(self, 200, {
|
||||
"ok": True,
|
||||
"filePath": file_path,
|
||||
})
|
||||
return
|
||||
|
||||
if self.path == "/append-account-log":
|
||||
file_path = append_account_log(
|
||||
payload.get("email"),
|
||||
@@ -690,6 +791,7 @@ def main():
|
||||
server = ThreadingHTTPServer((HOST, PORT), HotmailHelperHandler)
|
||||
print(f"Hotmail helper listening on http://{HOST}:{PORT}", flush=True)
|
||||
print(f"Account log file: {ACCOUNT_LOG_PATH}", flush=True)
|
||||
print(f"Account snapshot file: {ACCOUNT_RECORDS_SNAPSHOT_PATH}", flush=True)
|
||||
try:
|
||||
server.serve_forever()
|
||||
except KeyboardInterrupt:
|
||||
|
||||
Reference in New Issue
Block a user