Files
CheckInApp/check_in_worker.py
T
2025-11-06 23:10:20 +08:00

193 lines
8.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import requests
import configparser
import csv
import json
import time
import os
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from shared_config import (
CONFIG_INI_PATH, CONFIG_PATH, CONFIG_FILE_LOCK, get_logger,
CHROME_BINARY_PATH, CHROMEDRIVER_PATH
)
from email_notifier import send_success_notification, send_failure_notification
logger = get_logger(__name__)
def read_configs():
"""线程安全地读取配置文件"""
with CONFIG_FILE_LOCK:
if not os.path.exists(CONFIG_PATH):
return []
with open(CONFIG_PATH, mode='r', encoding='utf-8-sig') as file:
return list(csv.DictReader(file))
def get_live_x_api_payload(auth_token):
"""
启动一个临时的无头浏览器会话,只为了获取新鲜的 x-api-request-payload。
"""
logger.info("正在启动临时浏览器会话以监听网络日志...")
service = Service(executable_path=CHROMEDRIVER_PATH) if CHROMEDRIVER_PATH else Service()
chrome_options = Options()
chrome_options.binary_location = CHROME_BINARY_PATH
# --- 1. (最关键) 开启性能日志记录功能 ---
# 这会让浏览器记录下所有的网络事件
logging_prefs = {'performance': 'ALL'}
chrome_options.set_capability('goog:loggingPrefs', logging_prefs)
# --- Headless模式配置 ---
user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36"
chrome_options.add_argument(f'user-agent={user_agent}')
chrome_options.add_argument("--headless")
chrome_options.add_argument("--no-sandbox")
chrome_options.add_argument("--disable-dev-shm-usage")
chrome_options.add_argument("--window-size=1920,1080")
chrome_options.add_argument('--ignore-certificate-errors')
chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"])
driver = webdriver.Chrome(service=service, options=chrome_options)
payload_signature = None
try:
# 1. 导航到一个同源空白页,用于设置Cookie
driver.get("https://i.jielong.com/my-class")
# 3. 注入我们的长期Token
driver.add_cookie({
'name': 'token',
'value': auth_token,
'domain': '.jielong.com'
})
# 4. 导航到触发API的页面,这将产生网络日志
driver.get("https://i.jielong.com/my-form")
# 5. 等待几秒,确保页面有足够的时间加载并发起API请求
max_wait_time = 20 # 最多等待20秒
start_time = time.time()
found = False
while time.time() - start_time < max_wait_time:
logs = driver.get_log('performance')
for entry in logs:
log = json.loads(entry['message'])['message']
if log['method'] == 'Network.requestWillBeSent':
headers = log.get('params', {}).get('request', {}).get('headers', {})
headers_lower = {k.lower(): v for k, v in headers.items()}
if 'x-api-request-payload' in headers_lower:
payload_signature = headers_lower['x-api-request-payload']
logger.info("成功通过网络日志捕获到现场的 x-api-request-payload")
found = True
break
if found:
break
time.sleep(1) # 每次轮询间隔1秒
if not payload_signature:
raise Exception(f"{max_wait_time} 秒内未能通过网络日志捕获到 x-api-request-payload。")
except Exception as e:
logger.error(f"获取现场 x-api-request-payload 时失败: {e}")
driver.save_screenshot(os.path.join(os.path.dirname(__file__), 'payload_debug.png'))
finally:
driver.quit()
return payload_signature
def perform_check_in(config):
logger.info(f"Selenium打卡: 正在为 Signature: {config['Signature']} 执行打卡...")
auth_token = config.get('Authorization')
if not auth_token:
logger.error(f"Signature: {config['Signature']} 的长期Token为空,跳过。")
return
payload_signature = get_live_x_api_payload(auth_token)
if not payload_signature:
logger.error(f"Signature: {config['Signature']} 未能获取到现场签名,打卡中止。")
return
email_settings = None
if config.get('email'):
if os.path.exists(CONFIG_INI_PATH):
config_parser = configparser.ConfigParser()
config_parser.read(CONFIG_INI_PATH)
# 使用 .get() 安全访问
if 'Email' in config_parser:
email_settings = config_parser['Email']
else:
logger.warning("在 config.ini 中找不到 [Email] 配置段,无法发送邮件。")
else:
logger.warning("找不到 config.ini,无法发送邮件通知。")
try:
payload = {
"Id": 0,
"ThreadId": config['ThreadId'],
"Number": "",
"Signature": config['Signature'],
"RecordValues": [{
"FieldId": 1,
"Values": [config['Values']],
"Texts": [config['Texts']],
"HasValue": True,
"Scores": [],
"Files": [],
"MatrixValues": [],
"CustomTableValues": [],
"FillInMatrixFieldValues": [],
"MatrixFormValues": []
}],
"DateTarget": "",
"IsNeedManualAudit": False,
"MinuteTarget": -1,
"IsNameNumberComfirm": False
}
headers = {
'User-Agent': "Mozilla%2f5.0+(Linux%3b+Android+16%3b+wv)+AppleWebKit%2f537.36+(KHTML%2c+like+Gecko)+Chrome%2f142.0.0.0+Safari%2f537.36+QQ%2f9.2.30.31620+QQ%2fMiniApp",
'Accept-Encoding': "gzip",
'Content-Type': "application/json",
'authorization': f"Bearer {auth_token}",
'x-api-request-referer': "https://appservice.qq.com/1110276759",
'x-api-request-payload': payload_signature,
'referer': "https://appservice.qq.com/1110276759/8.10.1.7/page-frame.html",
'platform': "qq",
'x-api-request-mode': "cors",
}
url = "https://api.jielong.com/api/CheckIn/EditRecord"
response = requests.post(url, data=json.dumps(payload), headers=headers)
response.raise_for_status()
response_text = response.text
logger.info(f"Signature: {config['Signature']} 打卡请求完成!响应: {response_text}")
# logger.info(f"payload = {payload}")
# logger.info(f"headers = {headers}")
# logger.info(f"url = {url}")
# 判断响应内容
if email_settings:
if "打卡成功" in response_text:
logger.info(f"检测到成功关键字,为 {config['Signature']} 发送成功邮件...")
send_success_notification(config, email_settings)
elif "QSfqFrHF0jbMZcd3DVuvf6k5HceMjOlDwzX1b/SJ4agLnRkO" in response_text: # 打卡失败附带的Data
logger.warning(f"检测到登录失败关键字,为 {config['Signature']} 发送失败提醒邮件...")
send_failure_notification(config, email_settings)
# 检查HTTP状态码,如果需要的话
response.raise_for_status()
return response.text
except requests.exceptions.RequestException as e:
logger.error(f"为 Signature: {config['Signature']} 打卡时请求失败: {e}")
if e.response is not None:
logger.error(f" 响应状态码: {e.response.status_code}, 响应内容: {e.response.text}")
return e.response.text # 同样返回响应文本
return None # 请求彻底失败
except Exception as e:
logger.error(f"为 Signature: {config['Signature']} 打卡时发生未知错误: {e}")
return None