# cron "0 9 2 * * *
# const $ = new Env('恩山签到')
# -*- coding: utf-8 -*-
import os
import re
import time
import random
import requests
from DrissionPage import ChromiumPage, ChromiumOptions
USER_AGENT = "Mozilla/5.0 (Linux; Android 13; SM-G981B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Mobile Safari/537.36"
CACHE_FILE = "enshan_users.txt" # Cookie 缓存文件
def random_wait():
delay = random.randint(0, 900)
print(f"🎲 随机延迟启动: 将在 {delay} 秒后开始执行任务...")
time.sleep(delay)
print("⏰ 倒计时结束,任务开始!")
def force_kill_chrome():
print("🧹 正在清理残留的浏览器进程...")
try:
os.system("pkill -f chromium")
os.system("pkill -f chrome")
time.sleep(2)
except:
pass
def push_pushplus(token, content):
if not token:
return
url = "https://www.pushplus.plus/send"
data = {"token": token, "title": "恩山签到结果", "content": content}
try:
requests.post(url, json=data)
print("📨 PushPlus 通知已发送")
except Exception as e:
print(f"❌ 推送失败: {e}")
def get_cookies_safe(page):
try:
ret = page.run_cdp('Network.getCookies')
cookies_list = ret.get('cookies', [])
return "; ".join([f"{item['name']}={item['value']}" for item in cookies_list])
except Exception as e:
print(f"❌ 获取 Cookie 异常: {e}")
return ""
def extract_regex(pattern, text, default="0"):
try:
match = re.search(pattern, text)
return match.group(1).strip() if match else default
except:
return default
def load_cache():
"""从本地缓存文件读取 {uid: cookie} 字典"""
cache = {}
if not os.path.exists(CACHE_FILE):
return cache
with open(CACHE_FILE, 'r', encoding='utf-8') as f:
for line in f:
line = line.strip()
if not line or line.startswith('#'):
continue
if '#' in line:
parts = line.split('#', 1)
uid = parts[0].strip()
cookie = parts[1].strip()
if uid and cookie:
cache[uid] = cookie
return cache
def save_cache(cache):
"""将 {uid: cookie} 字典写入缓存文件"""
with open(CACHE_FILE, 'w', encoding='utf-8') as f:
for uid, cookie in cache.items():
f.write(f"{uid}#{cookie}\n")
print(f"💾 Cookie 缓存已更新至 {CACHE_FILE}")
def parse_env_users():
"""从环境变量 ENS_CK 解析用户列表,返回 {uid: cookie} 字典"""
ens_ck = os.environ.get('ENS_CK')
if not ens_ck:
return {}
users = {}
for line in ens_ck.strip().splitlines():
line = line.strip()
if not line or line.startswith('#'):
continue
if '#' in line:
parts = line.split('#', 1)
uid = parts[0].strip()
cookie = parts[1].strip()
if uid and cookie:
users[uid] = cookie
return users
def sign_for_user(uid, cookie, push_token):
"""执行单用户签到,返回 (成功否, 消息, 新cookie)"""
print(f"\n========== 开始处理用户 UID: {uid} ==========")
co = ChromiumOptions()
co.set_argument('--no-sandbox')
co.set_argument('--disable-gpu')
co.set_argument('--disable-dev-shm-usage')
co.set_argument('--headless=new')
co.set_argument('--window-size=375,812')
if os.path.exists("/usr/bin/chromium-browser"):
co.set_paths(browser_path="/usr/bin/chromium-browser")
elif os.path.exists("/usr/bin/chromium"):
co.set_paths(browser_path="/usr/bin/chromium")
co.set_user_agent(user_agent=USER_AGENT)
page = None
for attempt in range(2):
try:
force_kill_chrome()
page = ChromiumPage(co)
break
except Exception as e:
print(f"⚠️ 浏览器启动失败 (第 {attempt+1} 次尝试): {e}")
time.sleep(3)
if not page:
error_msg = "浏览器连续启动失败"
print(f"❌ {error_msg}")
push_pushplus(push_token, f"用户 {uid} 签到失败:{error_msg}")
return False, error_msg, cookie
try:
print("1. 访问主页确立作用域...")
page.get('https://www.right.com.cn/forum/forum.php?mobile=2')
try:
page.set.cookies(cookie)
except:
pass
print("2. 刷新页面并过盾...")
page.refresh()
time.sleep(5)
title = page.title
if "安全" in title or "验证" in title:
print("🛡️ 检测到防火墙拦截,正在等待自动跳转...")
time.sleep(15)
print("3. 正在获取签到信息...")
check_url = "https://www.right.com.cn/forum/erling_qd-sign_in_m.html"
page.get(check_url)
time.sleep(3)
is_signed = False
html = page.html
formhash = extract_regex(r"var FORMHASH = '([0-9a-zA-Z]+)'", html, "")
if not formhash:
formhash = extract_regex(r'name="formhash" value="([0-9a-zA-Z]+)"', html, "")
if not formhash:
formhash = extract_regex(r'formhash=([0-9a-zA-Z]+)', html, "")
if not formhash:
try:
body_text = page.ele('tag:body').text
if "登录" in body_text or "Login" in body_text:
error_msg = "Cookie 已失效,变为游客状态"
print(f"❌ {error_msg}")
push_pushplus(push_token, f"用户 {uid} 签到失败:{error_msg},请更新环境变量 ENS_CK 中的 Cookie。")
return False, error_msg, cookie
except:
pass
try:
body_text = page.ele('tag:body').text
if "连续签到" in body_text and "立即签到" not in body_text:
is_signed = True
print("ℹ️ 状态: 今天已经签到过了。")
except:
pass
if not formhash and not is_signed:
error_msg = "无法提取 formhash (可能页面结构改变)"
print(f"❌ {error_msg}")
push_pushplus(push_token, f"用户 {uid} 签到失败:{error_msg}")
return False, error_msg, cookie
if formhash:
print(f"🔑 获取 Formhash 成功: {formhash}")
sign_success = False
sign_msg = "已签到"
if not is_signed:
sign_api = "https://www.right.com.cn/forum/plugin.php?id=erling_qd:action&action=sign"
print("🚀 正在发送签到请求...")
js_code = f"""
return fetch("{sign_api}", {{
method: "POST",
headers: {{
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
"X-Requested-With": "XMLHttpRequest"
}},
body: "formhash={formhash}"
}}).then(response => response.json());
"""
try:
result = page.run_js(js_code)
print(f"📥 签到接口返回: {result}")
if result and (result.get('success') or "已经签到" in str(result)):
sign_success = True
sign_msg = result.get('message', '签到成功')
else:
sign_msg = result.get('message', '未知错误') if result else "接口无响应"
except Exception as js_err:
print(f"❌ JS 执行异常: {js_err}")
sign_success = False
sign_msg = "JS执行失败或WAF拦截"
else:
sign_success = True
if sign_success:
print("4. 正在获取最终积分数据...")
page.get(check_url)
time.sleep(2)
sign_html = page.html
today_points = extract_regex(r'erqd-current-point[^>]*>(\d+)', sign_html, "未知")
if today_points == "未知":
today_points = extract_regex(r'今日积分.*?(\d+)', sign_html, "未知")
continuous_days = extract_regex(r'erqd-continuous-days[^>]*>(\d+)', sign_html, "未知")
if continuous_days == "未知":
continuous_days = extract_regex(r'连续签到.*?(\d+)', sign_html, "未知")
total_days = extract_regex(r'erqd-total-days[^>]*>(\d+)', sign_html, "未知")
if total_days == "未知":
total_days = extract_regex(r'总签到天数.*?(\d+)', sign_html, "未知")
print("🔄 正在刷新积分缓存...")
credit_log_url = "https://www.right.com.cn/forum/home.php?mod=spacecp&ac=credit&op=log&mobile=2"
page.get(credit_log_url)
time.sleep(2)
profile_url = f"https://www.right.com.cn/forum/home.php?mod=space&uid={uid}&do=profile&mycenter=1&mobile=2"
print(f"📥 正在抓取个人资料页 (UID: {uid})...")
page.get(profile_url)
total_points = "未知"
contribution = "未知"
enshan_coin = "未知"
try:
time.sleep(5)
all_lis = page.eles('tag:li')
for li in all_lis:
clean_text = li.text.replace(" ", "").replace("\n", "").replace("\r", "")
if not clean_text:
continue
if ("积分" in clean_text and "今日" not in clean_text) or "Points" in clean_text:
match_cn = re.search(r'(\d+)积分', clean_text)
match_en = re.search(r'(\d+)Points', clean_text)
if match_cn:
total_points = match_cn.group(1)
elif match_en:
total_points = match_en.group(1)
if "贡献" in clean_text or "Contributions" in clean_text:
match_cn = re.search(r'(\d+)分贡献', clean_text)
match_en = re.search(r'(\d+)pointsContributions', clean_text)
if match_cn:
contribution = match_cn.group(1)
elif match_en:
contribution = match_en.group(1)
if "恩山币" in clean_text or "EnshanCoin" in clean_text:
match_cn = re.search(r'(\d+)币恩山币', clean_text)
match_en = re.search(r'(\d+)coinsEnshanCoin', clean_text)
if match_cn:
enshan_coin = match_cn.group(1)
elif match_en:
enshan_coin = match_en.group(1)
print(f"📊 抓取结果: 积分={total_points}, 贡献={contribution}, 币={enshan_coin}")
except Exception as e:
print(f"❌ 数据解析异常: {e}")
new_cookie = get_cookies_safe(page)
if not new_cookie:
new_cookie = cookie
notify_content = (
f"✅ 用户 {uid} 签到成功!🎊
"
f"📊 积分统计如下:
"
f"===========
"
f"今日积分:{today_points}
"
f"连续签到:{continuous_days} 天
"
f"总签到天数:{total_days} 天
"
f"总积分:{total_points}
"
f"贡献分:{contribution} 分
"
f"恩山币:{enshan_coin} 币"
)
print("=== 推送内容预览 ===")
print(notify_content.replace("
", "\n"))
push_pushplus(push_token, notify_content)
return True, "签到成功", new_cookie
else:
error_msg = f"签到失败:{sign_msg}"
print(f"❌ {error_msg}")
push_pushplus(push_token, f"用户 {uid} 签到失败:{sign_msg}")
return False, error_msg, cookie
except Exception as e:
import traceback
traceback.print_exc()
error_msg = f"运行异常: {str(e)}"
push_pushplus(push_token, f"用户 {uid} 签到异常:{error_msg}")
return False, error_msg, cookie
finally:
try:
if page:
page.quit()
except:
pass
force_kill_chrome()
def main():
random_wait()
# 1. 从环境变量读取账号列表(权威来源)
env_users = parse_env_users()
if not env_users:
print("❌ 环境变量 ENS_CK 未设置或为空,无法继续。")
return
# 2. 加载本地 Cookie 缓存
cache = load_cache()
# 3. 合并:优先使用缓存,若缓存不存在或环境变量中的 Cookie 与缓存不同,则使用环境变量(视为手动更新)
# 我们以 env_users 为基准,遍历每个 UID,决定使用的 Cookie
final_users = {} # {uid: cookie_to_use}
for uid, env_cookie in env_users.items():
if uid in cache:
cached_cookie = cache[uid]
# 如果环境变量中的 cookie 与缓存不同,则使用环境变量(用户可能手动更新)
if env_cookie != cached_cookie:
print(f"ℹ️ 用户 {uid} 的环境变量 Cookie 与缓存不同,将使用环境变量(视为手动更新)。")
final_users[uid] = env_cookie
else:
final_users[uid] = cached_cookie
else:
final_users[uid] = env_cookie # 新用户,首次使用环境变量
print(f"📋 共加载 {len(final_users)} 个用户,开始逐个签到...")
push_token = os.environ.get('PUSHPLUS_TOKEN', '')
new_cache = {} # 用于存储处理后的最新 Cookie
for idx, (uid, cookie) in enumerate(final_users.items(), 1):
print(f"\n>>> 正在处理第 {idx}/{len(final_users)} 个用户 (UID: {uid})")
ok, msg, new_cookie = sign_for_user(uid, cookie, push_token)
# 无论签到是否成功,只要有新 cookie(可能是旧 cookie 或更新后的),都保存
# 如果签到成功,new_cookie 是新的;如果失败,new_cookie 是原 cookie(或原样)
new_cache[uid] = new_cookie
if idx < len(final_users):
time.sleep(5)
# 4. 将更新后的所有 Cookie 写回缓存文件
save_cache(new_cache)
# 汇总信息
success_count = sum(1 for u in final_users if u in new_cache and new_cache[u] != '')
print(f"\n📊 完成,共 {len(final_users)} 个用户,成功 {success_count} 个。")
if __name__ == "__main__":
main()