# Source: https://github.com/CN-Grace/QinglongScripts/blob/main/Airport.py # Raw: https://raw.githubusercontent.com/CN-Grace/QinglongScripts/main/Airport.py # Repo: CN-Grace/QinglongScripts # Path: Airport.py # UploadedAt: 2026-05-23T15:48:24Z # SHA256: 8d929ef7cd9c6942b81d31e420758e46f8ca649407c505dc3567b84385f5e6f4 # Category: APP版/账密 # Evidence: username/password/mobile/login #!/usr/bin/env python3 """ cron: 0 0 * * * new Env("机场签到") 多服务机场自动签到脚本 - 自动登录多个指定服务网站 - 执行每日签到任务 - 获取剩余流量信息 - 发送格式化报告到通知渠道 """ import os import re import sys import time import base64 import requests from lxml import html from typing import Dict, Any, List, Tuple from utils import log_info, log_success, log_warning, log_error, beijing_time_str, create_session from notifier import send as notify_send # ==================== 服务配置 ==================== SERVICES = [ { "name": "速鹰666", "base_url": "https://suying00.com", "login_path": "/auth/login", "checkin_path": "/user/checkin", "user_info_path": "/user", "traffic_xpath": '//*[@id="app"]/div/div[3]/section/div[3]/div[2]/div/div[2]/div[2]', "username": os.environ.get("SUYING_USERNAME", ""), "password": os.environ.get("SUYING_PASSWORD", ""), } ] REQUEST_TIMEOUT = 10 # ==================== 功能函数 ==================== def login_to_service(service_config: Dict[str, str]) -> Tuple[bool, Any]: """登录到服务网站""" session = create_session() session.headers.update({"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"}) login_url = f"{service_config['base_url']}{service_config['login_path']}" login_data = {"email": service_config["username"], "passwd": service_config["password"], "remember_me": "on", "code": ""} try: log_info(f"正在登录 [{service_config['name']}] 账户: {service_config['username']}") response = session.post(login_url, data=login_data, timeout=REQUEST_TIMEOUT) response.raise_for_status() login_success = False try: json_data = response.json() if json_data.get("ret") == 1 or json_data.get("success"): login_success = True log_success(f"[{service_config['name']}] 登录成功") except Exception: pass if not login_success: cookies = session.cookies.get_dict() if "uid" in cookies or "key" in cookies or "email" in cookies: login_success = True log_success(f"[{service_config['name']}] 登录成功") if not login_success and response.text: if any(kw in response.text.lower() for kw in ["登录成功", "login success", "success", "欢迎"]): login_success = True log_success(f"[{service_config['name']}] 登录成功") if login_success: return True, session log_error(f"[{service_config['name']}] 登录失败,响应: {response.text[:200]}") return False, "无法判断登录状态" except requests.exceptions.RequestException as e: log_error(f"[{service_config['name']}] 登录请求失败: {e}") return False, f"网络请求失败: {e}" except Exception as e: log_error(f"[{service_config['name']}] 登录未知错误: {e}") return False, f"未知错误: {e}" def perform_checkin(session, service_config: Dict[str, str]) -> Dict[str, Any]: """执行签到""" checkin_url = f"{service_config['base_url']}{service_config['checkin_path']}" result = {"success": False, "message": "签到失败", "details": {}} try: log_info(f"[{service_config['name']}] 正在执行签到...") response = session.post(checkin_url, timeout=REQUEST_TIMEOUT) response.raise_for_status() try: json_data = response.json() result["details"] = json_data result["message"] = json_data.get("msg", "签到完成") result["success"] = True log_success(f"[{service_config['name']}] 签到成功: {result['message']}") except ValueError: result["message"] = "签到完成,返回非标准格式" result["details"] = {"raw_response": response.text[:500]} result["success"] = True log_success(f"[{service_config['name']}] 签到完成") except requests.exceptions.RequestException as e: log_error(f"[{service_config['name']}] 签到请求失败: {e}") result["message"] = f"网络请求失败: {e}" except Exception as e: log_error(f"[{service_config['name']}] 签到未知错误: {e}") result["message"] = f"未知错误: {e}" return result def extract_and_decode_base64(html_content): """从 HTML 中提取 base64 内容并解码""" pattern = r'var\s+originBody\s*=\s*["\']([^"\']+)["\']' match = re.search(pattern, html_content) if match: try: decoded_bytes = base64.b64decode(match.group(1)) return decoded_bytes.decode("utf-8") except Exception as e: log_warning(f"Base64 解码失败: {e}") return None return None def get_traffic_info(session, service_config: Dict[str, str]) -> Dict[str, Any]: """获取剩余流量信息""" info_url = f"{service_config['base_url']}{service_config['user_info_path']}" result = {"success": False, "traffic": "获取失败", "raw_html": None, "error": None} try: log_info(f"[{service_config['name']}] 正在获取流量信息...") response = session.get(info_url, timeout=REQUEST_TIMEOUT) response.raise_for_status() raw_html = response.text result["raw_html"] = raw_html decoded_html = extract_and_decode_base64(raw_html) html_to_parse = decoded_html or raw_html tree = html.fromstring(html_to_parse) traffic_elements = tree.xpath(service_config["traffic_xpath"]) if traffic_elements: traffic_text = traffic_elements[0].text_content().strip() result["traffic"] = traffic_text result["success"] = True log_success(f"[{service_config['name']}] 流量信息: {traffic_text}") else: result["error"] = "XPath 未找到匹配元素" log_warning(f"[{service_config['name']}] {result['error']}") except requests.exceptions.RequestException as e: log_error(f"[{service_config['name']}] 获取流量请求失败: {e}") result["error"] = f"网络请求失败: {e}" except Exception as e: log_error(f"[{service_config['name']}] 解析流量未知错误: {e}") result["error"] = f"解析错误: {e}" return result def format_multi_checkin_report(results: List[Dict[str, Any]]) -> str: """格式化多服务签到报告""" lines = ["📋 多服务签到报告", "", f"执行时间: {beijing_time_str()}", ""] for idx, res in enumerate(results, 1): name = res["name"] checkin = res["checkin_result"] traffic = res["traffic_result"] login_error = res.get("login_error") lines.append(f"{idx}. {name}") if login_error: lines.append(f" 🔐 登录: ❌ 失败 — {login_error}") lines.append(f" 📝 签到: 跳过 | 📊 流量: 跳过") else: ci_emoji = "✅" if checkin["success"] else "❌" lines.append(f" 📝 签到: {ci_emoji} — {checkin['message']}") if traffic["success"]: lines.append(f" 📊 流量: ✅ {traffic['traffic']}") else: lines.append(f" 📊 流量: ❌ 获取失败") if traffic.get("error"): lines.append(f" 错误: {traffic['error']}") lines.append("") lines.append("─" * 18) lines.append(f"🕒 执行时间: {beijing_time_str()}") return "\n".join(lines) def save_debug_info(service_name, traffic_result, checkin_result): """保存调试信息""" if traffic_result.get("raw_html"): filename = f"debug_{service_name}_{beijing_time_str('%Y%m%d_%H%M%S')}.html" try: with open(filename, "w", encoding="utf-8") as f: f.write(f"=== 调试信息 ===\n服务: {service_name}\n时间: {beijing_time_str()}\n签到: {checkin_result}\n流量: {traffic_result.get('traffic', 'N/A')}\n\n=== 原始HTML ===\n{traffic_result['raw_html']}") log_info(f"调试信息已保存到: {filename}") return filename except Exception as e: log_error(f"保存调试信息失败: {e}") return None def main(): log_info("=" * 50) log_info("多服务自动签到脚本开始执行") log_info("=" * 50) start_time = time.time() results = [] for service in SERVICES: service_name = service["name"] log_info(f"--- 开始处理: {service_name} ---") login_success, login_result = login_to_service(service) if not login_success: log_error(f"[{service_name}] 登录失败: {login_result}") results.append({"name": service_name, "login_error": login_result, "checkin_result": None, "traffic_result": None}) continue session = login_result checkin_result = perform_checkin(session, service) traffic_result = get_traffic_info(session, service) results.append({"name": service_name, "login_error": None, "checkin_result": checkin_result, "traffic_result": traffic_result}) if not traffic_result["success"] and traffic_result.get("raw_html"): save_debug_info(service_name, traffic_result, checkin_result) log_info(f"--- {service_name} 处理完成 ---") report = format_multi_checkin_report(results) notify_send("机场签到报告", report) print(f"\n{'=' * 60}\n{report}\n{'=' * 60}") execution_time = time.time() - start_time log_info(f"脚本执行完成,耗时: {execution_time:.2f}秒") any_success = any(res.get("checkin_result") and res["checkin_result"]["success"] for res in results) return 0 if any_success else 1 if __name__ == "__main__": try: exit_code = main() except KeyboardInterrupt: log_info("用户中断执行") exit_code = 130 except Exception as e: log_error(f"脚本异常: {e}") notify_send("机场签到脚本异常", f"错误: {str(e)[:200]}") exit_code = 1 sys.exit(exit_code)