重构更新账号日志功能,添加开发者模式

This commit is contained in:
QLHazyCoder
2026-04-17 18:29:53 +08:00
parent 3b28845d86
commit ccea024f9a
16 changed files with 1245 additions and 458 deletions
+104 -2
View File
@@ -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: